9 "from notebook_preamble import J, V, define"
13 "cell_type": "markdown",
16 "# Square Spiral Example Joy Code"
20 "cell_type": "markdown",
24 "Here is the example of Joy code from the `README` file:\n",
26 " [[[abs]ii <=][[<>][pop !-]||]&&][[!-][[++]][[--]]ifte dip][[pop !-][--][++]ifte]ifte\n",
28 "It might seem unreadable but with a little familiarity it becomes just as\n",
29 "legible as any other notation. Some layout helps:\n",
33 " [<>] [pop !-] ||\n",
36 " [[ !-] [[++]] [[--]] ifte dip]\n",
37 " [[pop !-] [--] [++] ifte ]\n",
40 "This function accepts two integers on the stack and increments or\n",
41 "decrements one of them such that the new pair of numbers is the next\n",
42 "coordinate pair in a square spiral (like the kind used to construct an\n",
48 "cell_type": "markdown",
53 "It's adapted from [the original code on StackOverflow](https://stackoverflow.com/questions/398299/looping-in-a-spiral/31864777#31864777):\n",
56 "> If all you're trying to do is generate the first N points in the spiral\n",
57 "> (without the original problem's constraint of masking to an N x M\n",
58 "> region), the code becomes very simple:\n",
60 " void spiral(const int N)\n",
64 " for(int i = 0; i < N; ++i)\n",
66 " cout << x << '\\t' << y << '\\n';\n",
67 " if(abs(x) <= abs(y) && (x != y || x >= 0))\n",
68 " x += ((y >= 0) ? 1 : -1);\n",
70 " y += ((x >= 0) ? -1 : 1);\n",
76 "cell_type": "markdown",
79 "## Translation to Joy\n",
81 "I'm going to make a function that take two ints (`x` and `y`) and\n",
82 "generates the next pair, we'll turn it into a generator later using the\n",
87 "cell_type": "markdown",
90 "### First Boolean Predicate\n",
92 "We need a function that computes `abs(x) <= abs(y)`, we can use `ii` to\n",
93 "apply `abs` to both values and then compare them\n",
98 "I've defined two short-circuiting Boolean combinators `&&` and `||` that\n",
99 "each accept two quoted predicate programs, run the first, and\n",
100 "conditionally run the second only if required (to compute the final\n",
101 "Boolean value). They run their predicate arguments `nullary`."
106 "execution_count": 2,
110 "define('&& [nullary] cons [nullary [0]] dip branch')\n",
111 "define('|| [nullary] cons [nullary] dip [1] branch')"
115 "cell_type": "markdown",
118 "Given those, we can define `x != y || x >= 0` as:\n",
120 " [<>] [pop 0 >=] ||\n",
122 "And `(abs(x) <= abs(y) && (x != y || x >= 0))` as:\n",
124 " [[abs] ii <=] [[<>] [pop 0 >=] ||] &&\n",
126 "It's a little rough, but, as I say, with a little familiarity it becomes\n",
131 "cell_type": "markdown",
134 "### The Increment / Decrement Branches\n",
136 "Turning to the branches of the main `if` statement:\n",
138 " x += ((y >= 0) ? 1 : -1);\n",
140 "Rewrite as a hybrid (pseudo-code) `ifte` expression:\n",
142 " [y >= 0] [x += 1] [X -= 1] ifte\n",
144 "Change each C phrase to Joy code:\n",
146 " [0 >=] [[++] dip] [[--] dip] ifte\n",
148 "Factor out the dip from each branch:\n",
150 " [0 >=] [[++]] [[--]] ifte dip\n",
152 "Similar logic applies to the other branch:\n",
154 " y += ((x >= 0) ? -1 : 1);\n",
156 " [x >= 0] [y -= 1] [y += 1] ifte\n",
158 " [pop 0 >=] [--] [++] ifte"
162 "cell_type": "markdown",
165 "### \"Not Negative\""
170 "execution_count": 3,
178 "cell_type": "markdown",
181 "## Putting the Pieces Together\n",
183 "We can assemble the three functions we just defined in quotes and give\n",
184 "them them to the `ifte` combinator. With some arrangement to show off\n",
185 "the symmetry of the two branches, we have:\n",
187 " [[[abs] ii <=] [[<>] [pop !-] ||] &&]\n",
188 " [[ !-] [[++]] [[--]] ifte dip]\n",
189 " [[pop !-] [--] [++] ifte ]\n",
192 "As I was writing this up I realized that, since the `&&` combinator\n",
193 "doesn't consume the stack (below its quoted args), I can unquote the\n",
194 "predicate, swap the branches, and use the `branch` combinator instead of\n",
197 " [[abs] ii <=] [[<>] [pop !-] ||] &&\n",
198 " [[pop !-] [--] [++] ifte ]\n",
199 " [[ !-] [[++]] [[--]] ifte dip]\n",
205 "execution_count": 4,
209 "define('spiral_next [[[abs] ii <=] [[<>] [pop !-] ||] &&] [[!-] [[++]] [[--]] ifte dip] [[pop !-] [--] [++] ifte] ifte')"
213 "cell_type": "markdown",
221 "execution_count": 5,
226 "output_type": "stream",
233 "J('0 0 spiral_next')"
238 "execution_count": 6,
243 "output_type": "stream",
250 "J('1 0 spiral_next')"
255 "execution_count": 7,
260 "output_type": "stream",
267 "J('1 -1 spiral_next')"
272 "execution_count": 8,
277 "output_type": "stream",
284 "J('0 -1 spiral_next')"
288 "cell_type": "markdown",
291 "## Turning it into a Generator with `x`\n",
293 "It can be used with the x combinator to make a kind of generator for\n",
294 "spiral square coordinates.\n",
297 "We can use `codireco` to make a generator\n",
299 " codireco ::= cons dip rest cons\n",
301 "It will look like this:\n",
303 " [value [F] codireco]\n",
305 "Here's a trace of how it works:\n",
307 " [0 [dup ++] codireco] . x\n",
308 " [0 [dup ++] codireco] . 0 [dup ++] codireco\n",
309 " [0 [dup ++] codireco] 0 . [dup ++] codireco\n",
310 " [0 [dup ++] codireco] 0 [dup ++] . codireco\n",
311 " [0 [dup ++] codireco] 0 [dup ++] . cons dip rest cons\n",
312 " [0 [dup ++] codireco] [0 dup ++] . dip rest cons\n",
313 " . 0 dup ++ [0 [dup ++] codireco] rest cons\n",
314 " 0 . dup ++ [0 [dup ++] codireco] rest cons\n",
315 " 0 0 . ++ [0 [dup ++] codireco] rest cons\n",
316 " 0 1 . [0 [dup ++] codireco] rest cons\n",
317 " 0 1 [0 [dup ++] codireco] . rest cons\n",
318 " 0 1 [[dup ++] codireco] . cons\n",
319 " 0 [1 [dup ++] codireco] . "
323 "cell_type": "markdown",
326 "But first we have to change the `spiral_next` function to work on a\n",
327 "quoted pair of integers, and leave a copy of the pair on the stack.\n",
330 " y x spiral_next\n",
331 " ---------------------\n",
336 " [x y] [spiral_next] infra\n",
337 " -------------------------------\n",
343 "execution_count": 9,
348 "output_type": "stream",
355 "J('[0 0] [spiral_next] infra')"
359 "cell_type": "markdown",
362 "So our generator is:\n",
364 " [[x y] [dup [spiral_next] infra] codireco]\n",
368 " [[0 0] [dup [spiral_next] infra] codireco]\n",
370 "There is a function `make_generator` that will build the generator for us\n",
371 "out of the value and stepper function:\n",
373 " [0 0] [dup [spiral_next] infra] make_generator\n",
374 " ----------------------------------------------------\n",
375 " [[0 0] [dup [spiral_next] infra] codireco]"
379 "cell_type": "markdown",
382 "Here it is in action:"
387 "execution_count": 10,
392 "output_type": "stream",
394 "[0 0] [0 1] [-1 1] [-1 0]\n"
399 "J('[0 0] [dup [spiral_next] infra] make_generator x x x x pop')"
403 "cell_type": "markdown",
406 "Four `x` combinators, four pairs of coordinates."
410 "cell_type": "markdown",
415 "So that's an example of Joy code. It's a straightforward translation of\n",
416 "the original. It's a little long for a single definition, you might\n",
417 "break it up like so:\n",
419 " _spn_P ::= [[abs] ii <=] [[<>] [pop !-] ||] &&\n",
421 " _spn_T ::= [ !-] [[++]] [[--]] ifte dip\n",
422 " _spn_E ::= [pop !-] [--] [++] ifte\n",
424 " spiral_next ::= _spn_P [_spn_E] [_spn_T] branch\n",
426 "This way it's easy to see that the function is a branch with two\n",
427 "quasi-symmetrical paths.\n",
429 "We then used this function to make a simple generator of coordinate\n",
430 "pairs, where the next pair in the series can be generated at any time by\n",
431 "using the `x` combinator on the generator (which is just a quoted\n",
432 "expression containing a copy of the current pair and the \"stepper\n",
433 "function\" to generate the next pair from that.)"
438 "execution_count": 11,
442 "define('_spn_P [[abs] ii <=] [[<>] [pop !-] ||] &&')\n",
443 "define('_spn_T [!-] [[++]] [[--]] ifte dip')\n",
444 "define('_spn_E [pop !-] [--] [++] ifte')\n",
445 "define('spiral_next _spn_P [_spn_E] [_spn_T] branch')"
450 "execution_count": 12,
457 "output_type": "stream",
459 " . 23 18 spiral_next\n",
460 " 23 . 18 spiral_next\n",
461 " 23 18 . spiral_next\n",
462 " 23 18 . _spn_P [_spn_E] [_spn_T] branch\n",
463 " 23 18 . [[abs] ii <=] [[<>] [pop !-] ||] && [_spn_E] [_spn_T] branch\n",
464 " 23 18 [[abs] ii <=] . [[<>] [pop !-] ||] && [_spn_E] [_spn_T] branch\n",
465 " 23 18 [[abs] ii <=] [[<>] [pop !-] ||] . && [_spn_E] [_spn_T] branch\n",
466 " 23 18 [[abs] ii <=] [[<>] [pop !-] ||] . [nullary] cons [nullary [0]] dip branch [_spn_E] [_spn_T] branch\n",
467 " 23 18 [[abs] ii <=] [[<>] [pop !-] ||] [nullary] . cons [nullary [0]] dip branch [_spn_E] [_spn_T] branch\n",
468 " 23 18 [[abs] ii <=] [[[<>] [pop !-] ||] nullary] . [nullary [0]] dip branch [_spn_E] [_spn_T] branch\n",
469 "23 18 [[abs] ii <=] [[[<>] [pop !-] ||] nullary] [nullary [0]] . dip branch [_spn_E] [_spn_T] branch\n",
470 " 23 18 [[abs] ii <=] . nullary [0] [[[<>] [pop !-] ||] nullary] branch [_spn_E] [_spn_T] branch\n",
471 " 23 18 [[abs] ii <=] . [stack] dinfrirst [0] [[[<>] [pop !-] ||] nullary] branch [_spn_E] [_spn_T] branch\n",
472 " 23 18 [[abs] ii <=] [stack] . dinfrirst [0] [[[<>] [pop !-] ||] nullary] branch [_spn_E] [_spn_T] branch\n",
473 " 23 18 [[abs] ii <=] [stack] . dip infra first [0] [[[<>] [pop !-] ||] nullary] branch [_spn_E] [_spn_T] branch\n",
474 " 23 18 . stack [[abs] ii <=] infra first [0] [[[<>] [pop !-] ||] nullary] branch [_spn_E] [_spn_T] branch\n",
475 " 23 18 [18 23] . [[abs] ii <=] infra first [0] [[[<>] [pop !-] ||] nullary] branch [_spn_E] [_spn_T] branch\n",
476 " 23 18 [18 23] [[abs] ii <=] . infra first [0] [[[<>] [pop !-] ||] nullary] branch [_spn_E] [_spn_T] branch\n",
477 " 23 18 . [abs] ii <= [18 23] swaack first [0] [[[<>] [pop !-] ||] nullary] branch [_spn_E] [_spn_T] branch\n",
478 " 23 18 [abs] . ii <= [18 23] swaack first [0] [[[<>] [pop !-] ||] nullary] branch [_spn_E] [_spn_T] branch\n",
479 " 23 18 [abs] . [dip] dupdip i <= [18 23] swaack first [0] [[[<>] [pop !-] ||] nullary] branch [_spn_E] [_spn_T] branch\n",
480 " 23 18 [abs] [dip] . dupdip i <= [18 23] swaack first [0] [[[<>] [pop !-] ||] nullary] branch [_spn_E] [_spn_T] branch\n",
481 " 23 18 [abs] . dip [abs] i <= [18 23] swaack first [0] [[[<>] [pop !-] ||] nullary] branch [_spn_E] [_spn_T] branch\n",
482 " 23 . abs 18 [abs] i <= [18 23] swaack first [0] [[[<>] [pop !-] ||] nullary] branch [_spn_E] [_spn_T] branch\n",
483 " 23 . 18 [abs] i <= [18 23] swaack first [0] [[[<>] [pop !-] ||] nullary] branch [_spn_E] [_spn_T] branch\n",
484 " 23 18 . [abs] i <= [18 23] swaack first [0] [[[<>] [pop !-] ||] nullary] branch [_spn_E] [_spn_T] branch\n",
485 " 23 18 [abs] . i <= [18 23] swaack first [0] [[[<>] [pop !-] ||] nullary] branch [_spn_E] [_spn_T] branch\n",
486 " 23 18 . abs <= [18 23] swaack first [0] [[[<>] [pop !-] ||] nullary] branch [_spn_E] [_spn_T] branch\n",
487 " 23 18 . <= [18 23] swaack first [0] [[[<>] [pop !-] ||] nullary] branch [_spn_E] [_spn_T] branch\n",
488 " False . [18 23] swaack first [0] [[[<>] [pop !-] ||] nullary] branch [_spn_E] [_spn_T] branch\n",
489 " False [18 23] . swaack first [0] [[[<>] [pop !-] ||] nullary] branch [_spn_E] [_spn_T] branch\n",
490 " 23 18 [False] . first [0] [[[<>] [pop !-] ||] nullary] branch [_spn_E] [_spn_T] branch\n",
491 " 23 18 False . [0] [[[<>] [pop !-] ||] nullary] branch [_spn_E] [_spn_T] branch\n",
492 " 23 18 False [0] . [[[<>] [pop !-] ||] nullary] branch [_spn_E] [_spn_T] branch\n",
493 " 23 18 False [0] [[[<>] [pop !-] ||] nullary] . branch [_spn_E] [_spn_T] branch\n",
494 " 23 18 . 0 [_spn_E] [_spn_T] branch\n",
495 " 23 18 0 . [_spn_E] [_spn_T] branch\n",
496 " 23 18 0 [_spn_E] . [_spn_T] branch\n",
497 " 23 18 0 [_spn_E] [_spn_T] . branch\n",
499 " 23 18 . [pop !-] [--] [++] ifte\n",
500 " 23 18 [pop !-] . [--] [++] ifte\n",
501 " 23 18 [pop !-] [--] . [++] ifte\n",
502 " 23 18 [pop !-] [--] [++] . ifte\n",
503 " 23 18 [pop !-] [--] [++] . [nullary not] dipd branch\n",
504 " 23 18 [pop !-] [--] [++] [nullary not] . dipd branch\n",
505 " 23 18 [pop !-] . nullary not [--] [++] branch\n",
506 " 23 18 [pop !-] . [stack] dinfrirst not [--] [++] branch\n",
507 " 23 18 [pop !-] [stack] . dinfrirst not [--] [++] branch\n",
508 " 23 18 [pop !-] [stack] . dip infra first not [--] [++] branch\n",
509 " 23 18 . stack [pop !-] infra first not [--] [++] branch\n",
510 " 23 18 [18 23] . [pop !-] infra first not [--] [++] branch\n",
511 " 23 18 [18 23] [pop !-] . infra first not [--] [++] branch\n",
512 " 23 18 . pop !- [18 23] swaack first not [--] [++] branch\n",
513 " 23 . !- [18 23] swaack first not [--] [++] branch\n",
514 " 23 . 0 >= [18 23] swaack first not [--] [++] branch\n",
515 " 23 0 . >= [18 23] swaack first not [--] [++] branch\n",
516 " True . [18 23] swaack first not [--] [++] branch\n",
517 " True [18 23] . swaack first not [--] [++] branch\n",
518 " 23 18 [True] . first not [--] [++] branch\n",
519 " 23 18 True . not [--] [++] branch\n",
520 " 23 18 False . [--] [++] branch\n",
521 " 23 18 False [--] . [++] branch\n",
522 " 23 18 False [--] [++] . branch\n",
529 "V('23 18 spiral_next')"
535 "display_name": "Python 2",
536 "language": "python",
544 "file_extension": ".py",
545 "mimetype": "text/x-python",
547 "nbconvert_exporter": "python",
548 "pygments_lexer": "ipython3",