1 Square Spiral Example Joy Code
2 ==============================
4 Here is the example of Joy code from the ``README`` file:
8 [[[abs]ii <=][[<>][pop !-]||]&&][[!-][[++]][[--]]ifte dip][[pop !-][--][++]ifte]ifte
10 It might seem unreadable but with a little familiarity it becomes just
11 as legible as any other notation. Some layout helps:
20 [[ !-] [[++]] [[--]] ifte dip]
21 [[pop !-] [--] [++] ifte ]
24 This function accepts two integers on the stack and increments or
25 decrements one of them such that the new pair of numbers is the next
26 coordinate pair in a square spiral (like the kind used to construct an
32 It’s adapted from `the original code on
33 StackOverflow <https://stackoverflow.com/questions/398299/looping-in-a-spiral/31864777#31864777>`__:
35 If all you’re trying to do is generate the first N points in the
36 spiral (without the original problem’s constraint of masking to an N
37 x M region), the code becomes very simple:
41 void spiral(const int N)
45 for(int i = 0; i < N; ++i)
47 cout << x << '\t' << y << '\n';
48 if(abs(x) <= abs(y) && (x != y || x >= 0))
49 x += ((y >= 0) ? 1 : -1);
51 y += ((x >= 0) ? -1 : 1);
58 I’m going to make a function that take two ints (``x`` and ``y``) and
59 generates the next pair, we’ll turn it into a generator later using the
62 First Boolean Predicate
63 ~~~~~~~~~~~~~~~~~~~~~~~
65 We need a function that computes ``abs(x) <= abs(y)``, we can use ``ii``
66 to apply ``abs`` to both values and then compare them with ``<=``:
74 [_p [abs] ii <=] inscribe
117 Short-Circuiting Boolean Combinators
118 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
120 I’ve defined two short-circuiting Boolean combinators ``&&`` and ``||``
121 that each accept two quoted predicate programs, run the first, and
122 conditionally run the second only if required (to compute the final
123 Boolean value). They run their predicate arguments ``nullary``.
127 [&& [nullary] cons [nullary [false]] dip branch] inscribe
128 [|| [nullary] cons [nullary] dip [true] branch] inscribe
184 Translating the Conditionals
185 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
187 Given those, we can define ``x != y || x >= 0`` as:
191 _a == [!=] [pop 0 >=] ||
195 [_a [!=] [pop 0 >=] ||] inscribe
202 And ``(abs(x) <= abs(y) && (x != y || x >= 0))`` as:
210 [_b [_p] [_a] &&] inscribe
217 It’s a little rough, but, as I say, with a little familiarity it becomes
237 23 -18 • [_p] [_a] &&
238 23 -18 [_p] • [_a] &&
239 23 -18 [_p] [_a] • &&
240 23 -18 [_p] [_a] • [nullary] cons [nullary [false]] dip branch
241 23 -18 [_p] [_a] [nullary] • cons [nullary [false]] dip branch
242 23 -18 [_p] [[_a] nullary] • [nullary [false]] dip branch
243 23 -18 [_p] [[_a] nullary] [nullary [false]] • dip branch
244 23 -18 [_p] • nullary [false] [[_a] nullary] branch
245 23 -18 [_p] • [stack] dinfrirst [false] [[_a] nullary] branch
246 23 -18 [_p] [stack] • dinfrirst [false] [[_a] nullary] branch
247 23 -18 [_p] [stack] • dip infrst [false] [[_a] nullary] branch
248 23 -18 • stack [_p] infrst [false] [[_a] nullary] branch
249 23 -18 [-18 23] • [_p] infrst [false] [[_a] nullary] branch
250 23 -18 [-18 23] [_p] • infrst [false] [[_a] nullary] branch
251 23 -18 [-18 23] [_p] • infra first [false] [[_a] nullary] branch
252 23 -18 • _p [-18 23] swaack first [false] [[_a] nullary] branch
253 23 -18 • [abs] ii <= [-18 23] swaack first [false] [[_a] nullary] branch
254 23 -18 [abs] • ii <= [-18 23] swaack first [false] [[_a] nullary] branch
255 23 • abs -18 abs <= [-18 23] swaack first [false] [[_a] nullary] branch
256 23 • -18 abs <= [-18 23] swaack first [false] [[_a] nullary] branch
257 23 -18 • abs <= [-18 23] swaack first [false] [[_a] nullary] branch
258 23 18 • <= [-18 23] swaack first [false] [[_a] nullary] branch
259 false • [-18 23] swaack first [false] [[_a] nullary] branch
260 false [-18 23] • swaack first [false] [[_a] nullary] branch
261 23 -18 [false] • first [false] [[_a] nullary] branch
262 23 -18 false • [false] [[_a] nullary] branch
263 23 -18 false [false] • [[_a] nullary] branch
264 23 -18 false [false] [[_a] nullary] • branch
279 The Increment / Decrement Branches
280 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
282 Turning to the branches of the main ``if`` statement:
286 x += ((y >= 0) ? 1 : -1);
288 Rewrite as a hybrid (pseudo-code) ``ifte`` expression:
292 [y >= 0] [x += 1] [X -= 1] ifte
294 Change each C phrase to Joy code:
298 [0 >=] [[++] dip] [[--] dip] ifte
300 Factor out the dip from each branch:
304 [0 >=] [[++]] [[--]] ifte dip
306 Similar logic applies to the other branch:
310 y += ((x >= 0) ? -1 : 1);
312 [x >= 0] [y -= 1] [y += 1] ifte
314 [pop 0 >=] [--] [++] ifte
316 Putting the Pieces Together
317 ---------------------------
319 We can assemble the three functions we just defined in quotes and give
320 them them to the ``ifte`` combinator. With some arrangement to show off
321 the symmetry of the two branches, we have:
325 [[[abs] ii <=] [[<>] [pop !-] ||] &&]
326 [[ !-] [[++]] [[--]] ifte dip]
327 [[pop !-] [--] [++] ifte ]
335 [[ !-] [[++]] [[--]] ifte dip]
336 [[pop !-] [--] [++] ifte ]
346 As I was writing this up I realized that, since the ``&&`` combinator
347 doesn’t consume the stack (below its quoted args), I can unquote the
348 predicate, swap the branches, and use the ``branch`` combinator instead
353 [[abs] ii <=] [[<>] [pop !-] ||] &&
354 [[pop !-] [--] [++] ifte ]
355 [[ !-] [[++]] [[--]] ifte dip]
504 Turning it into a Generator with ``x``
505 --------------------------------------
507 It can be used with the x combinator to make a kind of generator for
508 spiral square coordinates.
510 We can use ``codireco`` to make a generator
514 codireco == cons dip rest cons
516 It will look like this:
522 Here’s a trace of how it works:
528 [0 [dup ++] codireco] [x] trace
533 [0 [dup ++] codireco] • x
534 [0 [dup ++] codireco] • 0 [dup ++] codireco
535 [0 [dup ++] codireco] 0 • [dup ++] codireco
536 [0 [dup ++] codireco] 0 [dup ++] • codireco
537 [0 [dup ++] codireco] 0 [dup ++] • codi reco
538 [0 [dup ++] codireco] 0 [dup ++] • cons dip reco
539 [0 [dup ++] codireco] [0 dup ++] • dip reco
540 • 0 dup ++ [0 [dup ++] codireco] reco
541 0 • dup ++ [0 [dup ++] codireco] reco
542 0 0 • ++ [0 [dup ++] codireco] reco
543 0 1 • [0 [dup ++] codireco] reco
544 0 1 [0 [dup ++] codireco] • reco
545 0 1 [0 [dup ++] codireco] • rest cons
546 0 1 [[dup ++] codireco] • cons
547 0 [1 [dup ++] codireco] •
549 0 [1 [dup ++] codireco]
560 But first we have to change the ``spiral_next`` function to work on a
561 quoted pair of integers, and leave a copy of the pair on the stack.
567 ---------------------
574 [x y] [spiral_next] infra
575 -------------------------------
580 [0 0] [spiral_next] infra
591 [[x y] [dup [spiral_next] infra] codireco]
597 [[0 0] [dup [spiral_next] infra] codireco]
599 There is a function ``make_generator`` that will build the generator for
600 us out of the value and stepper function:
604 [0 0] [dup [spiral_next] infra] make_generator
605 ----------------------------------------------------
606 [[0 0] [dup [spiral_next] infra] codireco]
617 Here it is in action:
621 [0 0] [dup [spiral_next] infra] make_generator x x x x pop
626 [0 0] [0 1] [-1 1] [-1 0]
628 Four ``x`` combinators, four pairs of coordinates.
630 Or you can leave out ``dup`` and let the value stay in the generator
637 [0 0] [[spiral_next] infra] make_generator 50 [x] times first
647 So that’s an example of Joy code. It’s a straightforward translation of
648 the original. It’s a little long for a single definition, you might
653 _spn_Pa == [abs] ii <=
654 _spn_Pb == [!=] [pop 0 >=] ||
655 _spn_P == [_spn_Pa] [_spn_Pb] &&
657 _spn_T == [ !-] [[++]] [[--]] ifte dip
658 _spn_E == [pop !-] [--] [++] ifte
660 spiral_next == _spn_P [_spn_E] [_spn_T] branch
662 This way it’s easy to see that the function is a branch with two
663 quasi-symmetrical paths.
665 We then used this function to make a simple generator of coordinate
666 pairs, where the next pair in the series can be generated at any time by
667 using the ``x`` combinator on the generator (which is just a quoted
668 expression containing a copy of the current pair and the “stepper
669 function” to generate the next pair from that.)