OSDN Git Service

Recover the square spiral example code.
[joypy/Thun.git] / docs / Square_Spiral.ipynb
1 {
2  "cells": [
3   {
4    "cell_type": "code",
5    "execution_count": 1,
6    "metadata": {},
7    "outputs": [],
8    "source": [
9     "from notebook_preamble import J, V, define"
10    ]
11   },
12   {
13    "cell_type": "markdown",
14    "metadata": {},
15    "source": [
16     "# Square Spiral Example Joy Code"
17    ]
18   },
19   {
20    "cell_type": "markdown",
21    "metadata": {},
22    "source": [
23     "\n",
24     "Here is the example of Joy code from the `README` file:\n",
25     "\n",
26     "    [[[abs]ii <=][[<>][pop !-]||]&&][[!-][[++]][[--]]ifte dip][[pop !-][--][++]ifte]ifte\n",
27     "\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",
30     "\n",
31     "    [   [[abs] ii <=]\n",
32     "        [\n",
33     "            [<>] [pop !-] ||\n",
34     "        ] &&\n",
35     "    ]\n",
36     "    [[    !-] [[++]] [[--]] ifte dip]\n",
37     "    [[pop !-]  [--]   [++]  ifte    ]\n",
38     "    ifte\n",
39     "\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",
43     "Ulam Spiral).  \n",
44     "\n"
45    ]
46   },
47   {
48    "cell_type": "markdown",
49    "metadata": {},
50    "source": [
51     "## Original Form\n",
52     "\n",
53     "It's adapted from [the original code on StackOverflow](https://stackoverflow.com/questions/398299/looping-in-a-spiral/31864777#31864777):\n",
54     "\n",
55     "\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",
59     "\n",
60     "    void spiral(const int N)\n",
61     "    {\n",
62     "        int x = 0;\n",
63     "        int y = 0;\n",
64     "        for(int i = 0; i < N; ++i)\n",
65     "        {\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",
69     "            else\n",
70     "                y += ((x >= 0) ? -1 : 1);\n",
71     "        }\n",
72     "    }"
73    ]
74   },
75   {
76    "cell_type": "markdown",
77    "metadata": {},
78    "source": [
79     "## Translation to Joy\n",
80     "\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",
83     "`x` combinator."
84    ]
85   },
86   {
87    "cell_type": "markdown",
88    "metadata": {},
89    "source": [
90     "### First Boolean Predicate\n",
91     "\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",
94     "with `<=`:\n",
95     "\n",
96     "    [abs] ii <=\n",
97     "\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`."
102    ]
103   },
104   {
105    "cell_type": "code",
106    "execution_count": 2,
107    "metadata": {},
108    "outputs": [],
109    "source": [
110     "define('&& [nullary] cons [nullary [0]] dip branch')\n",
111     "define('|| [nullary] cons [nullary] dip [1] branch')"
112    ]
113   },
114   {
115    "cell_type": "markdown",
116    "metadata": {},
117    "source": [
118     "Given those, we can define `x != y || x >= 0` as:\n",
119     "\n",
120     "    [<>] [pop 0 >=] ||\n",
121     "\n",
122     "And `(abs(x) <= abs(y) && (x != y || x >= 0))` as:\n",
123     "\n",
124     "    [[abs] ii <=] [[<>] [pop 0 >=] ||] &&\n",
125     "\n",
126     "It's a little rough, but, as I say, with a little familiarity it becomes\n",
127     "legible."
128    ]
129   },
130   {
131    "cell_type": "markdown",
132    "metadata": {},
133    "source": [
134     "### The Increment / Decrement Branches\n",
135     "\n",
136     "Turning to the branches of the main `if` statement:\n",
137     "\n",
138     "    x += ((y >= 0) ? 1 : -1);\n",
139     "\n",
140     "Rewrite as a hybrid (pseudo-code) `ifte` expression:\n",
141     "\n",
142     "    [y >= 0] [x += 1] [X -= 1] ifte\n",
143     "\n",
144     "Change each C phrase to Joy code:\n",
145     "\n",
146     "    [0 >=] [[++] dip] [[--] dip] ifte\n",
147     "\n",
148     "Factor out the dip from each branch:\n",
149     "\n",
150     "    [0 >=] [[++]] [[--]] ifte dip\n",
151     "\n",
152     "Similar logic applies to the other branch:\n",
153     "\n",
154     "    y += ((x >= 0) ? -1 : 1);\n",
155     "\n",
156     "    [x >= 0] [y -= 1] [y += 1] ifte\n",
157     "\n",
158     "    [pop 0 >=] [--] [++] ifte"
159    ]
160   },
161   {
162    "cell_type": "markdown",
163    "metadata": {},
164    "source": [
165     "### \"Not Negative\""
166    ]
167   },
168   {
169    "cell_type": "code",
170    "execution_count": 3,
171    "metadata": {},
172    "outputs": [],
173    "source": [
174     "define('!- 0 >=')"
175    ]
176   },
177   {
178    "cell_type": "markdown",
179    "metadata": {},
180    "source": [
181     "## Putting the Pieces Together\n",
182     "\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",
186     "\n",
187     "    [[[abs] ii <=] [[<>] [pop !-] ||] &&]\n",
188     "    [[    !-] [[++]] [[--]] ifte dip]\n",
189     "    [[pop !-]  [--]   [++]  ifte    ]\n",
190     "    ifte\n",
191     "\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",
195     "`ifte`:\n",
196     "\n",
197     "    [[abs] ii <=] [[<>] [pop !-] ||] &&\n",
198     "    [[pop !-]  [--]   [++]  ifte    ]\n",
199     "    [[    !-] [[++]] [[--]] ifte dip]\n",
200     "    branch"
201    ]
202   },
203   {
204    "cell_type": "code",
205    "execution_count": 4,
206    "metadata": {},
207    "outputs": [],
208    "source": [
209     "define('spiral_next [[[abs] ii <=] [[<>] [pop !-] ||] &&] [[!-] [[++]] [[--]] ifte dip] [[pop !-] [--] [++] ifte] ifte')"
210    ]
211   },
212   {
213    "cell_type": "markdown",
214    "metadata": {},
215    "source": [
216     "Let's try it out:"
217    ]
218   },
219   {
220    "cell_type": "code",
221    "execution_count": 5,
222    "metadata": {},
223    "outputs": [
224     {
225      "name": "stdout",
226      "output_type": "stream",
227      "text": [
228       "1 0\n"
229      ]
230     }
231    ],
232    "source": [
233     "J('0 0 spiral_next')"
234    ]
235   },
236   {
237    "cell_type": "code",
238    "execution_count": 6,
239    "metadata": {},
240    "outputs": [
241     {
242      "name": "stdout",
243      "output_type": "stream",
244      "text": [
245       "1 -1\n"
246      ]
247     }
248    ],
249    "source": [
250     "J('1 0 spiral_next')"
251    ]
252   },
253   {
254    "cell_type": "code",
255    "execution_count": 7,
256    "metadata": {},
257    "outputs": [
258     {
259      "name": "stdout",
260      "output_type": "stream",
261      "text": [
262       "0 -1\n"
263      ]
264     }
265    ],
266    "source": [
267     "J('1 -1 spiral_next')"
268    ]
269   },
270   {
271    "cell_type": "code",
272    "execution_count": 8,
273    "metadata": {},
274    "outputs": [
275     {
276      "name": "stdout",
277      "output_type": "stream",
278      "text": [
279       "-1 -1\n"
280      ]
281     }
282    ],
283    "source": [
284     "J('0 -1 spiral_next')"
285    ]
286   },
287   {
288    "cell_type": "markdown",
289    "metadata": {},
290    "source": [
291     "## Turning it into a Generator with `x`\n",
292     "\n",
293     "It can be used with the x combinator to make a kind of generator for\n",
294     "spiral square coordinates.\n",
295     "\n",
296     "\n",
297     "We can use `codireco` to make a generator\n",
298     "\n",
299     "    codireco ::= cons dip rest cons\n",
300     "\n",
301     "It will look like this:\n",
302     "\n",
303     "    [value [F] codireco]\n",
304     "\n",
305     "Here's a trace of how it works:\n",
306     "\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] . "
320    ]
321   },
322   {
323    "cell_type": "markdown",
324    "metadata": {},
325    "source": [
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",
328     "From:\n",
329     "\n",
330     "       y x spiral_next\n",
331     "    ---------------------\n",
332     "            y' x'\n",
333     "\n",
334     "to:\n",
335     "\n",
336     "       [x y] [spiral_next] infra\n",
337     "    -------------------------------\n",
338     "               [x' y']"
339    ]
340   },
341   {
342    "cell_type": "code",
343    "execution_count": 9,
344    "metadata": {},
345    "outputs": [
346     {
347      "name": "stdout",
348      "output_type": "stream",
349      "text": [
350       "[0 1]\n"
351      ]
352     }
353    ],
354    "source": [
355     "J('[0 0] [spiral_next] infra')"
356    ]
357   },
358   {
359    "cell_type": "markdown",
360    "metadata": {},
361    "source": [
362     "So our generator is:\n",
363     "\n",
364     "    [[x y] [dup [spiral_next] infra] codireco]\n",
365     "\n",
366     "Or rather:\n",
367     "\n",
368     "    [[0 0] [dup [spiral_next] infra] codireco]\n",
369     "\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",
372     "\n",
373     "       [0 0] [dup [spiral_next] infra] make_generator\n",
374     "    ----------------------------------------------------\n",
375     "         [[0 0] [dup [spiral_next] infra] codireco]"
376    ]
377   },
378   {
379    "cell_type": "markdown",
380    "metadata": {},
381    "source": [
382     "Here it is in action:"
383    ]
384   },
385   {
386    "cell_type": "code",
387    "execution_count": 10,
388    "metadata": {},
389    "outputs": [
390     {
391      "name": "stdout",
392      "output_type": "stream",
393      "text": [
394       "[0 0] [0 1] [-1 1] [-1 0]\n"
395      ]
396     }
397    ],
398    "source": [
399     "J('[0 0] [dup [spiral_next] infra] make_generator x x x x pop')"
400    ]
401   },
402   {
403    "cell_type": "markdown",
404    "metadata": {},
405    "source": [
406     "Four `x` combinators, four pairs of coordinates."
407    ]
408   },
409   {
410    "cell_type": "markdown",
411    "metadata": {},
412    "source": [
413     "## Conclusion\n",
414     "\n",
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",
418     "\n",
419     "         _spn_P ::= [[abs] ii <=] [[<>] [pop !-] ||] &&\n",
420     "\n",
421     "         _spn_T ::= [    !-] [[++]] [[--]] ifte dip\n",
422     "         _spn_E ::= [pop !-]  [--]   [++]  ifte\n",
423     "\n",
424     "    spiral_next ::= _spn_P [_spn_E] [_spn_T] branch\n",
425     "\n",
426     "This way it's easy to see that the function is a branch with two\n",
427     "quasi-symmetrical paths.\n",
428     "\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.)"
434    ]
435   },
436   {
437    "cell_type": "code",
438    "execution_count": 11,
439    "metadata": {},
440    "outputs": [],
441    "source": [
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')"
446    ]
447   },
448   {
449    "cell_type": "code",
450    "execution_count": 12,
451    "metadata": {
452     "scrolled": true
453    },
454    "outputs": [
455     {
456      "name": "stdout",
457      "output_type": "stream",
458      "text": [
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",
498       "                                                         23 18 . _spn_E\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",
523       "                                                         23 18 . --\n",
524       "                                                         23 17 . \n"
525      ]
526     }
527    ],
528    "source": [
529     "V('23 18 spiral_next')"
530    ]
531   }
532  ],
533  "metadata": {
534   "kernelspec": {
535    "display_name": "Python 2",
536    "language": "python",
537    "name": "python2"
538   },
539   "language_info": {
540    "codemirror_mode": {
541     "name": "ipython",
542     "version": 3
543    },
544    "file_extension": ".py",
545    "mimetype": "text/x-python",
546    "name": "python",
547    "nbconvert_exporter": "python",
548    "pygments_lexer": "ipython3",
549    "version": "3.8.3"
550   }
551  },
552  "nbformat": 4,
553  "nbformat_minor": 2
554 }