4 ms·
Tinkering with APL (Dyalog) gave me one of my most mind-bending programming moments. dismal ← 10⊥(⌈/10⊥⍣¯1⊢) This is the complete solution to addition in th
by adityaathalye 3y ago
Tinkering with APL (Dyalog) gave me one of my most mind-bending programming moments.
dismal ← 10⊥(⌈/10⊥⍣¯1⊢)
This is the complete solution to addition in the framework of Dismal Arithmetic [1].
The pivotal idea there was the inverse of a function, and "trains". Until that moment of insight, I was fiddling about with dfns, which looks janky in comparison.
dismal ← {10(⊤⍣¯1)⍵}∘{⌈/⍵}∘{10(⊥⍣¯1)⍵}⊢
⍣¯1 is APL for "inverse". Aaron Hsu helped me understand [2] what was going on.
I feel like this kind of conceptual power is tacit programming at is finest. It blows my mind that this alien gobbledygook code still makes sense to me after not touching APL for years now. And I've only toyed around with the language.
[1] I was playing with the problem to compare Clojure and APL, just because. https://www.evalapply.org/posts/dismal-arithmetic-dyalog-apl-clojure/ https://www.evalapply.org/posts/dismal-arithmetic-dyalog-apl...
[2] https://www.sacrideo.us/decoding-inverses/ https://www.sacrideo.us/decoding-inverses/
(edit: fix typos, verbiage)
- prezjordan 3y agoI can't seem to figure out what you mean by inverse. Is the inversion of addition subtraction? Neither post seems to explain what it is (or maybe it assumes knowledge of ⊥ and ⊤?)
- lokedhs 3y agoGiven a function F that accepts an argument x and returns y, its inverse is another function which, when passed y will return x. For dyadic functions, the incerse argument is always the right side one. So negation is its own inverse. So the inverse of 2+ is indeed subtraction of 2.
- adityaathalye 3y agoAnd if anyone wants an absolute masterclass in tacit programming, have a look at Aaron's Co-dfns compiler. The README has extensive reference material. https://github.com/Co-dfns/Co-dfns/ https://github.com/Co-dfns/Co-dfns/
- thelastparadise 3y ago> dismal ← 10⊥(⌈/10⊥⍣¯1⊢) And what does that look like in C?
- LegionMammal978 3y agoIn traditional C style, something like unsigned dismal(unsigned x, unsigned y) { unsigned z; for (z = 0; x || y; x /= 10, y /= 10) z = z * 10 + (x % 10 > y % 10 ? x % 10 : y % 10); return z; }
- DrDroop 3y agoInteresting, never heart about dismal arithmetic before. Squint hard enough and it starts to look like tropical geometry. This is a different construction where you give the addition and multiplication operators in a polynomial a different meaning. I've been trying to have a better understand why this is useful. I know tropical geometry has been used to improve things like price discovery, but I never got to a better understanding as to why. I At any rate I would be curious to know what dismal arithmetic can do for me.
- tangus 3y ago>The pivotal idea there was the inverse of a function I was curious about this piece of unexplained cryptic code, so I did a little investigation to see what's going on. Unfortunately there aren't any revolutionary concepts here, just esoteric notation. I'll explain: To do that "dismal addition" thing, you need to split a number into digits and build a new number using the largest digit at each position. dismal(123, 321) = 323. APL gives you an operator to make a number out of a sequence of digits: that inverted T you see in OP's code. The left operand is the base. `inverted_T(10, [1, 2, 3]) = 123`. It gives you another operator to split a quantity into a hierarchy of units. That's the Tee you see in their second snippet. The left operand is a sequence of radixes. An inch is 2.54 cm, a foot is 12 inches, a yard is 3 feet. To transform 130 cm into ft/yd/in, you'd do: `Tee([3, 12, 2.54], 130) = [1, 1, 3.18]`. So, OP wanted to use this Tee operator to split a number into digits. The problem is, they don't know beforehand how many digits the number has! If it's 2 digits, they must do `Tee([10, 10], number)`. If it's 3, they must do `Tee([10, 10, 10], number)`. (Because `Tee([10, 10], 123) = [12, 3]`). So in the second snippet they tried to do some juggling to get the number of digits and use it in the Tee function (I guess). What OP really needs is the inverse function of inverted_T. And wouldn't you know it, APL can give you the inverse of a built-in function or a sufficiently simple user function. How? Maybe an operator? No... See that operator that looks like a puckered face? That operator applies the function to its left, as many times as the operand to its right, to whatever is to the right of the sideways T. BUT, if the right operand is negative, it applies the inverse of the left operand. Basically, the all-powerful "invert function" operation is hidden as a special case of another operator... In sum, here's my interpretation of OP's code in pseudocode: Using: encode(base, seq) = <base> inverted_T <seq> max(a, b) = a gamma b reduce(fn, seq) = <fn> slash <seq> superapply(fn, times, seq) = <fn> puckered <times> sideways_T <seq> let dismal = encode(10, reduce(max, superapply(encode(10), -1))) So, dismal([123, 321, 111]) applies the inverse of `encode(10)` one time to each sequence item, giving: [[1, 2, 3], [3, 2, 1], [1, 1, 1]] Reduces using max max(max([1, 2, 3], [3, 2, 1]), [1, 1, 1]) giving [3, 2, 3] and encodes it in base 10, giving 323. So that's it. Nice standard library, awful syntax. I think that OP's "epiphany" was finding a quirk in this esoteric language to counteract another quirk. Anyway, having satisfied my curiosity, I'm going to promptly forget everything about this :)
- 3y ago