Excerpted and adapted from a longer writeup I posted to the J forums in 2009:
while =: ^:break_clause^:_
Here's an adverb you can apply to any code (which would equivalent of the
loop body) to create a while loop. In case you haven't seen it before, ^:
is the power conjunction. More specifically, the phrase f^:n y
applies the function f
to the argument y
exactly n
times. The count n
maybe be an integer or a function which applied to y
produces an integer¹.
In the adverb above, we see the power conjunction twice, once in ^:break_clause
and again in ^:_
. Let's first discuss the latter. That _
is J's notation for infinity. So, read literally, ^:_
is "apply the function an infinite number of times" or "keep reapplying forever". This is related to a while-loop's function, but it's not very useful if applied literally.
So, instead, ^:_
and its kin were defined to mean "apply a function to its limit", that is, "keep applying the function until its output matches its input". In that case, applying the function again would have no effect, because the next iteration would have the same input as the previous (remember that J is a functional language). So there's
no point in applying the function even once more: it has reached its limit.
For example:
cos=: 2&o. NB. Cosine function
pi =: 1p1 NB. J's notation for 1*pi^1 analogous to scientific notation 1e1
cos pi
_1
cos cos cos pi
0.857553
cos^:3 pi
0.857553
cos^:10 pi
0.731404
cos^:_ pi NB. Fixed point of cosine
0.739085
Here, we keep applying cosine until the answer stops changing: cosine has reached its fixed point, and more applications are superfluous. We can visualize this by showing the
intermediate steps:
cos^:a: pi
3.1415926535897 _1 0.54030230586813 ...73 more... 0.73908513321512 0.73908513321
So ^:_
applies a function to its limit. OK, what about ^:break_condition
? Again, it's the same concept: apply the function on the left the number of times specified by the function on the right. In the case of _
(or its function-equivalent, _:
) the output is "infinity", in the case of break_condition
the output will be 0
or 1
depending on the input (a break condition is boolean).
So if the input is "right" (i.e. processing is done), then the break_condition
will be 0
, whence loop_body^:break_condition^:_
will become loop_body^:0^:_
. Obviously, loop_body^:0
applies the loop_body
zero times, which has no effect.
To "have no effect" is to leave the input untouched; put another way, it copies the input to the output ... but if the input matches the output, then the function has reached its limit! Obviously ^:_:
detects this fact and terminates. Voila, a while loop!
¹ Yes, including zero and negative integers, and "an integer" should be more properly read as "an arbitrary array of integers" (so the function can be applied at more than one power simultaneously).
v
specifies how many times repeatf
(and if it's negative, you get the inverse off
). Perfect! I also understood whya:
works reading again the dictionary. Basically it translates intof^:i.k
for some integerk
and that's why you get multiple results: an array is passed to^:
. – Mir