I recently stumbled across APL and J, as mentioned in a previous post, and began reading a bit of an introductory book.
A couple of days ago, I came across this series of coding tasks on a blog called "Programming Praxis", and I picked the most recent one as an example to try out some J noobism.
The example is simple but contrived (and median is a terrible name) – we basically have to write a function that that takes two arguments and either increments, decrements or does nothing, with the first argument. In J, like so many other tasks, this is essentially one line (12 characters, to be precise).
median =: [ + (* @: -)~
To give some idea on what's going on here, this expression can be broken, first, into three parts: [
, +
, (* @: -)~
. Let's start with the sub-expression within the parentheses: * @: -
.
Here, *
is a verb (think: "function") that returns 1, 0, or -1 depending on the sign of its argument. So * 4
is 1
, * _2
(that's how -2 is represented) is _1
, * 0
is 0
.
-
is the only thing that does what you would expect: 4 - 3
is 1
, and so on.
@:
is a conjunction, such that f @: g
when applied to x has the effect of f (g x).
]
is a verb that when given two values, always selects the first of the two.
~
is an adverb that reverses the order of arguments to its preceding verb. So while %
applied to x and y yields (x % y), %~
applied to x and y yields (y % x).
Now things get a little tricky (but only a little; we're barely scratching the surface of J here). The expression for median can be seen as three verbs laid down together, and this is interpreted as a "train" of verbs, or more specifically in this case, a "dyadic fork".
Basically, (f g h) when applied to two values x and y, is equivalent to g applied to the result of f applied to x and y, and the result of h applied to x and y. Or, using infix notation,
x (f g h) y is equivalent to (x f y) g (x h y)
The reason x and y aren't present in the definition of median is that it's defined using a "tacit" form rather than an "explicit" form (somewhat similar to "point-free" definitions in Haskell).
Putting it all together now, when applied to two arguments, we get the equivalent of (x [ y) + (* (y - x))
, which gives x + 1
when y > x, x + 0
when y == x, and x + (-1)
when y < x, which is what we want.
If you like this sort of thing, head over to jsoftware.com for more.