OSDN Git Service

I narrowed down the bug.
[joypy/Thun.git] / docs / Recursion_Combinators.md
1 ```python
2 from notebook_preamble import D, DefinitionWrapper, J, V, define
3 ```
4
5 # Recursion Combinators
6
7 This article describes the `genrec` combinator, how to use it, and several generic specializations.
8
9                           [if] [then] [rec1] [rec2] genrec
10     ---------------------------------------------------------------------
11        [if] [then] [rec1 [[if] [then] [rec1] [rec2] genrec] rec2] ifte
12
13
14 From "Recursion Theory and Joy" (j05cmp.html) by Manfred von Thun:
15
16 > "The genrec combinator takes four program parameters in addition to
17 whatever data parameters it needs. Fourth from the top is an if-part,
18 followed by a then-part. If the if-part yields true, then the then-part
19 is executed and the combinator terminates. The other two parameters are
20 the rec1-part and the rec2-part. If the if-part yields false, the
21 rec1-part is executed. Following that the four program parameters and
22 the combinator are again pushed onto the stack bundled up in a quoted
23 form. Then the rec2-part is executed, where it will find the bundled
24 form. Typically it will then execute the bundled form, either with i or
25 with app2, or some other combinator."
26
27 ## Designing Recursive Functions
28 The way to design one of these is to fix your base case and 
29 test and then treat `R1` and `R2` as an else-part "sandwiching"
30 a quotation of the whole function.
31
32 For example, given a (general recursive) function `F`:
33
34     F == [I] [T] [R1]   [R2] genrec
35       == [I] [T] [R1 [F] R2] ifte
36
37 If the `[I]` predicate is false you must derive `R1` and `R2` from:
38
39     ... R1 [F] R2
40
41 Set the stack arguments in front and figure out what `R1` and `R2`
42 have to do to apply the quoted `[F]` in the proper way.
43
44 ## Primitive Recursive Functions
45 Primitive recursive functions are those where `R2 == i`.
46
47     P == [I] [T] [R] primrec
48       == [I] [T] [R [P] i] ifte
49       == [I] [T] [R P] ifte
50
51 ## [Hylomorphism](https://en.wikipedia.org/wiki/Hylomorphism_%28computer_science%29)
52 A [hylomorphism](https://en.wikipedia.org/wiki/Hylomorphism_%28computer_science%29) is a recursive function `H :: A -> C` that converts a value of type `A` into a value of type `C` by means of:
53
54 - A generator `G :: A -> (B, A)`
55 - A combiner `F :: (B, C) -> C`
56 - A predicate `P :: A -> Bool` to detect the base case
57 - A base case value `c :: C`
58 - Recursive calls (zero or more); it has a "call stack in the form of a cons list".
59
60 It may be helpful to see this function implemented in imperative Python code.
61
62
63 ```python
64 def hylomorphism(c, F, P, G):
65     '''Return a hylomorphism function H.'''
66
67     def H(a):
68         if P(a):
69             result = c
70         else:
71             b, aa = G(a)
72             result = F(b, H(aa))  # b is stored in the stack frame during recursive call to H().
73         return result
74
75     return H
76 ```
77
78 Cf. ["Bananas, Lenses, & Barbed Wire"](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.41.125)
79
80 Note that during evaluation of `H()` the intermediate `b` values are stored in the Python call stack.  This is what is meant by "call stack in the form of a cons list".
81
82 ## Hylomorphism in Joy
83 We can define a combinator `hylomorphism` that will make a hylomorphism combinator `H` from constituent parts.
84
85     H == [P] c [G] [F] hylomorphism
86
87 The function `H` is recursive, so we start with `ifte` and set the else-part to
88 some function `J` that will contain a quoted copy of `H`.  (The then-part just
89 discards the leftover `a` and replaces it with the base case value `c`.)
90
91     H == [P] [pop c] [J] ifte
92
93 The else-part `J` gets just the argument `a` on the stack.
94
95     a J
96     a G              The first thing to do is use the generator G
97     aa b             which produces b and a new aa
98     aa b [H] dip     we recur with H on the new aa
99     aa H b F         and run F on the result.
100
101 This gives us a definition for `J`.
102
103     J == G [H] dip F
104
105 Plug it in and convert to genrec.
106
107     H == [P] [pop c] [G [H] dip F] ifte
108     H == [P] [pop c] [G]   [dip F] genrec
109
110 This is the form of a hylomorphism in Joy, which nicely illustrates that
111 it is a simple specialization of the general recursion combinator.
112
113     H == [P] c [G] [F] hylomorphism == [P] [pop c] [G] [dip F] genrec
114
115 ## Derivation of `hylomorphism` combinator
116
117 Now we just need to derive a definition that builds the `genrec` arguments
118 out of the pieces given to the `hylomorphism` combinator.
119
120        [P]      c  [G]     [F] hylomorphism
121     ------------------------------------------
122        [P] [pop c] [G] [dip F] genrec
123
124 Working in reverse:
125
126 - Use `swoncat` twice to decouple `[c]` and `[F]`.
127 - Use `unit` to dequote `c`.
128 - Use `dipd` to untangle `[unit [pop] swoncat]` from the givens.
129
130 So:
131
132     H == [P] [pop c]              [G]                  [dip F] genrec
133          [P] [c]    [pop] swoncat [G]        [F] [dip] swoncat genrec
134          [P] c unit [pop] swoncat [G]        [F] [dip] swoncat genrec
135          [P] c [G] [F] [unit [pop] swoncat] dipd [dip] swoncat genrec
136
137 At this point all of the arguments (givens) to the hylomorphism are to the left so we have
138 a definition for `hylomorphism`:
139
140     hylomorphism == [unit [pop] swoncat] dipd [dip] swoncat genrec
141
142
143 ```python
144 define('hylomorphism == [unit [pop] swoncat] dipd [dip] swoncat genrec')
145 ```
146
147 ### Example: Finding [Triangular Numbers](https://en.wikipedia.org/wiki/Triangular_number)
148 Let's write a function that, given a positive integer, returns the sum of all positive integers less than that one.  (In this case the types `A`, `B` and `C` are all `int`.)
149
150 To sum a range of integers from 0 to *n* - 1:
151
152 - `[P]` is `[1 <=]`
153 - `c` is `0`
154 - `[G]` is `[-- dup]`
155 - `[F]` is `[+]`
156
157
158 ```python
159 define('triangular_number == [1 <=] 0 [-- dup] [+] hylomorphism')
160 ```
161
162 Let's try it:
163
164
165 ```python
166 J('5 triangular_number')
167 ```
168
169     10
170
171
172
173 ```python
174 J('[0 1 2 3 4 5 6] [triangular_number] map')
175 ```
176
177     [0 0 1 3 6 10 15]
178
179
180 ## Four Specializations
181 There are at least four kinds of recursive combinator, depending on two choices.  The first choice is whether the combiner function `F` should be evaluated during the recursion or pushed into the pending expression to be "collapsed" at the end.  The second choice is whether the combiner needs to operate on the current value of the datastructure or the generator's output, in other words, whether `F` or `G` should run first in the recursive branch.
182
183     H1 ==        [P] [pop c] [G             ] [dip F] genrec
184     H2 == c swap [P] [pop]   [G [F]    dip  ] [i]     genrec
185     H3 ==        [P] [pop c] [  [G] dupdip  ] [dip F] genrec
186     H4 == c swap [P] [pop]   [  [F] dupdip G] [i]     genrec
187
188 The working of the generator function `G` differs slightly for each.  Consider the recursive branches:
189
190     ... a G [H1] dip F                w/ a G == a′ b
191     
192     ... c a G [F] dip H2                 a G == b  a′
193     
194     ... a [G] dupdip [H3] dip F          a G == a′
195     
196     ... c a [F] dupdip G H4              a G == a′
197
198 The following four sections illustrate how these work, omitting the predicate evaluation.
199
200 ### `H1`
201
202     H1 == [P] [pop c] [G] [dip F] genrec
203
204 Iterate n times.
205
206     ... a  G [H1] dip F
207     ... a′ b [H1] dip F
208     ... a′ H1 b F
209     ... a′ G [H1] dip F b F
210     ... a″ b′ [H1] dip F b F
211     ... a″ H1 b′ F b F
212     ... a″ G [H1] dip F b′ F b F
213     ... a‴ b″ [H1] dip F b′ F b F
214     ... a‴ H1 b″ F b′ F b F
215     ... a‴ pop c b″ F b′ F b F
216     ... c b″ F b′ F b F
217     ... d      b′ F b F
218     ... d′          b F
219     ... d″
220
221 This form builds up a pending expression (continuation) that contains the intermediate results along with the pending combiner functions.  When the base case is reached the last term is replaced by the identity value `c` and the continuation "collapses" into the final result using the combiner `F`.
222
223 ### `H2`
224 When you can start with the identity value `c` on the stack and the combiner `F` can operate as you go using the intermediate results immediately rather than queuing them up, use this form.  An important difference is that the generator function must return its results in the reverse order.
225
226     H2 == c swap [P] [pop] [G [F] dip] primrec
227
228     ... c a G  [F] dip H2
229     ... c b a′ [F] dip H2
230     ... c b F a′ H2
231     ... d     a′ H2
232     ... d a′ G  [F] dip H2
233     ... d b′ a″ [F] dip H2
234     ... d b′ F a″ H2
235     ... d′     a″ H2
236     ... d′ a″ G  [F] dip H2
237     ... d′ b″ a‴ [F] dip H2
238     ... d′ b″ F a‴ H2
239     ... d″      a‴ H2
240     ... d″ a‴ pop
241     ... d″
242
243
244 ### `H3`
245 If you examine the traces above you'll see that the combiner `F` only gets to operate on the results of `G`, it never "sees" the first value `a`.  If the combiner and the generator both need to work on the current value then `dup` must be used, and the generator must produce one item instead of two (the b is instead the duplicate of a.)
246
247
248     H3 == [P] [pop c] [[G] dupdip] [dip F] genrec
249
250     ... a [G] dupdip [H3] dip F
251     ... a  G  a      [H3] dip F
252     ... a′    a      [H3] dip F
253     ... a′ H3 a               F
254     ... a′ [G] dupdip [H3] dip F a F
255     ... a′  G  a′     [H3] dip F a F
256     ... a″     a′     [H3] dip F a F
257     ... a″ H3  a′              F a F
258     ... a″ [G] dupdip [H3] dip F a′ F a F
259     ... a″  G    a″   [H3] dip F a′ F a F
260     ... a‴       a″   [H3] dip F a′ F a F
261     ... a‴ H3    a″            F a′ F a F
262     ... a‴ pop c a″ F a′ F a F
263     ...        c a″ F a′ F a F
264     ...        d      a′ F a F
265     ...        d′          a F
266     ...        d″
267
268 ### `H4`
269 And, last but not least, if you can combine as you go, starting with `c`, and the combiner `F` needs to work on the current item, this is the form:
270
271     H4 == c swap [P] [pop] [[F] dupdip G] primrec
272
273     ... c  a  [F] dupdip G H4
274     ... c  a   F  a      G H4
275     ... d         a      G H4
276     ... d  a′              H4
277     ... d  a′ [F] dupdip G H4
278     ... d  a′  F  a′     G H4
279     ... d′        a′     G H4
280     ... d′ a″              H4
281     ... d′ a″ [F] dupdip G H4
282     ... d′ a″  F  a″     G H4
283     ... d″        a″     G H4
284     ... d″ a‴              H4
285     ... d″ a‴ pop
286     ... d″
287
288 ## Anamorphism
289 An anamorphism can be defined as a hylomorphism that uses `[]` for `c` and
290 `swons` for `F`.  An anamorphic function builds a list of values.
291
292     A == [P] [] [G] [swons] hylomorphism
293
294 ### `range` et. al.
295 An example of an anamorphism is the `range` function which generates the list of integers from 0 to *n* - 1 given *n*.
296
297 Each of the above variations can be used to make four slightly different `range` functions.
298
299 #### `range` with `H1`
300     H1 == [P]    [pop c]  [G]      [dip F]     genrec
301        == [0 <=] [pop []] [-- dup] [dip swons] genrec
302
303
304 ```python
305 define('range == [0 <=] [] [-- dup] [swons] hylomorphism')
306 ```
307
308
309 ```python
310 J('5 range')
311 ```
312
313     [4 3 2 1 0]
314
315
316 #### `range` with `H2`
317     H2 == c  swap [P]    [pop] [G      [F]     dip] primrec
318        == [] swap [0 <=] [pop] [-- dup [swons] dip] primrec
319
320
321 ```python
322 define('range_reverse == [] swap [0 <=] [pop] [-- dup [swons] dip] primrec')
323 ```
324
325
326 ```python
327 J('5 range_reverse')
328 ```
329
330     [0 1 2 3 4]
331
332
333 #### `range` with `H3`
334     H3 == [P]    [pop c]  [[G]  dupdip] [dip F]     genrec
335        == [0 <=] [pop []] [[--] dupdip] [dip swons] genrec
336
337
338 ```python
339 define('ranger == [0 <=] [pop []] [[--] dupdip] [dip swons] genrec')
340 ```
341
342
343 ```python
344 J('5 ranger')
345 ```
346
347     [5 4 3 2 1]
348
349
350 #### `range` with `H4`
351     H4 == c  swap [P]    [pop] [[F]     dupdip G ] primrec
352        == [] swap [0 <=] [pop] [[swons] dupdip --] primrec
353
354
355 ```python
356 define('ranger_reverse == [] swap [0 <=] [pop] [[swons] dupdip --] primrec')
357 ```
358
359
360 ```python
361 J('5 ranger_reverse')
362 ```
363
364     [1 2 3 4 5]
365
366
367 Hopefully this illustrates the workings of the variations.  For more insight you can run the cells using the `V()` function instead of the `J()` function to get a trace of the Joy evaluation.
368
369 ## Catamorphism
370 A catamorphism can be defined as a hylomorphism that uses `[uncons swap]` for `[G]`
371 and `[[] =]` (or just `[not]`) for the predicate `[P]`.  A catamorphic function tears down a list term-by-term and makes some new value.
372
373     C == [not] c [uncons swap] [F] hylomorphism
374
375
376 ```python
377 define('swuncons == uncons swap')  # Awkward name.
378 ```
379
380 An example of a catamorphism is the sum function.
381
382     sum == [not] 0 [swuncons] [+] hylomorphism
383
384
385 ```python
386 define('sum == [not] 0 [swuncons] [+] hylomorphism')
387 ```
388
389
390 ```python
391 J('[5 4 3 2 1] sum')
392 ```
393
394     15
395
396
397 ### The `step` combinator
398 The `step` combinator will usually be better to use than `catamorphism`.
399
400
401 ```python
402 J('[step] help')
403 ```
404
405     Run a quoted program on each item in a sequence.
406     ::
407     
408             ... [] [Q] . step
409          -----------------------
410                    ... .
411     
412     
413            ... [a] [Q] . step
414         ------------------------
415                  ... a . Q
416     
417     
418          ... [a b c] [Q] . step
419       ----------------------------------------
420                    ... a . Q [b c] [Q] step
421     
422     The step combinator executes the quotation on each member of the list
423     on top of the stack.
424     
425
426
427
428 ```python
429 define('sum == 0 swap [+] step')
430 ```
431
432
433 ```python
434 J('[5 4 3 2 1] sum')
435 ```
436
437     15
438
439
440 ## Example: Factorial Function
441
442 For the Factorial function:
443
444     H4 == c swap [P] [pop] [[F] dupdip G] primrec
445
446 With:
447
448     c == 1
449     F == *
450     G == --
451     P == 1 <=
452
453
454 ```python
455 define('factorial == 1 swap [1 <=] [pop] [[*] dupdip --] primrec')
456 ```
457
458
459 ```python
460 J('5 factorial')
461 ```
462
463     120
464
465
466 ## Example: `tails`
467 An example of a paramorphism for lists given in the ["Bananas..." paper](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.41.125) is `tails` which returns the list of "tails" of a list.
468
469         [1 2 3] tails
470     --------------------
471        [[] [3] [2 3]]
472     
473
474 We can build as we go, and we want `F` to run after `G`, so we use pattern `H2`:
475
476     H2 == c swap [P] [pop] [G [F] dip] primrec
477
478 We would use:
479
480     c == []
481     F == swons
482     G == rest dup
483     P == not
484
485
486 ```python
487 define('tails == [] swap [not] [pop] [rest dup [swons] dip] primrec')
488 ```
489
490
491 ```python
492 J('[1 2 3] tails')
493 ```
494
495     [[] [3] [2 3]]
496
497
498 ## Conclusion: Patterns of Recursion
499 Our story so far...
500
501
502 ### Hylo-, Ana-, Cata-
503
504     H == [P  ] [pop c ] [G          ] [dip F        ] genrec
505     A == [P  ] [pop []] [G          ] [dip swap cons] genrec
506     C == [not] [pop c ] [uncons swap] [dip F        ] genrec
507
508 ### Para-, ?-, ?-
509
510     P == c  swap [P  ] [pop] [[F        ] dupdip G          ] primrec
511     ? == [] swap [P  ] [pop] [[swap cons] dupdip G          ] primrec
512     ? == c  swap [not] [pop] [[F        ] dupdip uncons swap] primrec
513
514
515 ## Appendix: Fun with Symbols
516
517     |[ (c, F), (G, P) ]| == (|c, F|) • [(G, P)]
518
519 ["Bananas, Lenses, & Barbed Wire"](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.41.125)
520
521     (|...|)  [(...)]  [<...>]
522
523 I think they are having slightly too much fun with the symbols.  However, "Too much is always better than not enough."