See also: http://www.jsoftware.com/papers/DFSP.htm - impressive practical application of this notation. I've never seen a more succinct (yet still rigorous to the point of being executable) description of the Simplex algorithm, which is given as an example here.
I wish more people were familiar with Iverson's work (APL, J) and its descendents (mostly K). Everything else is so needlessly complex in comparison.
There is a steep learning curve, true - but it is well worth it.
I'm still on the far low end of the curve, but APL (I prefer APL due to its non-ASCIIness) has quite a beauty in it. For "play" I wrote a sample for a normal distribution (using Box-Müller, since it was simpler than other, faster algorithms.) Here's the code (u is a function returning a sample from a uniform between 0 and 1, so just calling u is a random number in [0,1]). The only non-self evident bits would be →(s≥1)/ (which means if s greater than 1, jump to whatever label is after the slash) and ⍟var which is Pi times var. APL is really neat, and this is not even an example of a nice, array-oriented algorithm.
∇a←norm;uu;vv;s
⍝ Normal distribution around 0 (using the Box-Muller transform in polar form
⍝ Local variables go in the header after semicolons
CHOOSE:
uu←u
vv←u
s←(uu * 2)+vv * 2
→(s≥1)/CHOOSE
→(s=0)/CHOOSE
m←(((¯2×⍟s)÷s) * 0.5)
a←uu×m
∇
Other things that are non-self-evident except to people already familiar with APL: * means exponentiation, not multiplication; and "⍟var which is Pi times var" is incorrect: it's actually log(var).
I have to confess that I don't find this sort of example very compelling; Box-Muller isn't much more than a one-liner in any language. I mean, here it is in C, not noted for its richly expressive terseness:
double norm(void) {
while (1) {
double u=rand01(), v=rand01();
double s = u*u+v*v;
if (0 < s && s <= 1) return u * sqrt(-2*log(s)/s);
}
}
(I assumed that we already had a rand01() function equivalent to your "u"; I suggest that in any nontrivial program the gain in brevity from the single-character name is outweighed by being unable to call a local variable "u" and, if you have a bunch of these, the cognitive load of remembering what all the single-character names mean.)
I bet the C implementation is distinctly easier to make sense of for most mathematicians and most programmers, and it's not even substantially longer. (APL: 94 non-blank characters. C: 111 non-blank characters. I remark that counting non-blank characters is about as favourable a metric for APL as one can reasonably ask for.) One can code-golf it further: combine the declaration of u,v with that of s (saves 5 non-blank characters --> 106); switch to your "u" instead of my "rand01" and rename u,v to (say) x,y (saves 10 characters --> 96); convert to do/while and use commas instead of {;;;} for body (saves 4 characters -> 92). That looks like this, and I think is still pretty easy to read:
double norm(void) {
double x,y,s;
do x=u(), y=u(), s=x*x+y*y; while (s==0 || s>1);
return x*sqrt(-2*log(s)/s);
}
(Note: character-counting is by hand and it's entirely possible I'm out by a couple of chars either way.)
[EDITED to fix formatting screwage; aargh, I hate HN's treatment of asterisks]
You are right, π times is the monadic "circle" function... Still trying to learn fluently all operators. And indeed, it's not a specially good example, but I don't have a lot of APL code written (the "nicest" being a Mandelbrot set one-liner), the "longest" being this one and a function to generate histograms, but way harder to read, since it uses quite a few operators:
∇a←n hist x
⍝ Histogram data from x in n buckets
s←x[⍋ x]
mx←¯1 ↑ s
mn←1 ↑ s
bk←mn+(⍳n)×(mx-mn)÷n
a←+/(~(↑bk)<s)
s←((↑bk)<s)/s
bk←1↓bk
LOOP:→(0=⍴bk)/END
a←a,+/(~(↑bk)<s)
s←((↑bk)<s)/s
bk←1↓bk
→LOOP
END:
∇
Not brilliant, and even at the time of writing (5 or 6 months ago) I had the feeling it may be possible to write it loopless or close enough. Variables in this one are still "global" since (even if it works) I was still checking it for possible bugs.
What interests me is the idea of using a larger character set to improve code readability [not readability in the lowest common denominator sense, nor in the high priest of Perl sense either]. Just in the sense of making conventions concise.
💉foo() // dependency injection
🌏eggs // geospatial value
I'm not advocating standardization [ala APL at the language level]. I am thinking about making sections of code better diagramatically.
Iverson was a great man. His specialized notation that was the undercarriage of APL changed many people's view of programming because he forced a more mathematical and consistent use of array notation, far beyond the simple subroutine notations used in FORTRAN at the time.
I wish more people were familiar with Iverson's work (APL, J) and its descendents (mostly K). Everything else is so needlessly complex in comparison.
There is a steep learning curve, true - but it is well worth it.