OSDN Git Service

7f273d8cd29883baff4ae4469ee524a7e585e879
[joypy/Thun.git] / docs / sphinx_docs / _build / html / _sources / notebooks / Treestep.rst.txt
1 Treating Trees II: ``treestep``
2 ===============================
3
4 Let’s consider a tree structure, similar to one described `“Why
5 functional programming matters” by John
6 Hughes <https://www.cs.kent.ac.uk/people/staff/dat/miranda/whyfp90.pdf>`__,
7 that consists of a node value followed by zero or more child trees. (The
8 asterisk is meant to indicate the `Kleene
9 star <https://en.wikipedia.org/wiki/Kleene_star>`__.)
10
11 ::
12
13    tree = [] | [node tree*]
14
15 In the spirit of ``step`` we are going to define a combinator
16 ``treestep`` which expects a tree and three additional items: a
17 base-case function ``[B]``, and two quoted programs ``[N]`` and ``[C]``.
18
19 ::
20
21    tree [B] [N] [C] treestep
22
23 If the current tree node is empty then just execute ``B``:
24
25 ::
26
27       [] [B] [N] [C] treestep
28    ---------------------------
29       []  B
30
31 Otherwise, evaluate ``N`` on the node value, ``map`` the whole function
32 (abbreviated here as ``K``) over the child trees recursively, and then
33 combine the result with ``C``.
34
35 ::
36
37       [node tree*] [B] [N] [C] treestep
38    --------------------------------------- w/ K == [B] [N] [C] treestep
39           node N [tree*] [K] map C
40
41 (Later on we’ll experiment with making ``map`` part of ``C`` so you can
42 use other combinators.)
43
44 Derive the recursive function.
45 ------------------------------
46
47 We can begin to derive it by finding the ``ifte`` stage that ``genrec``
48 will produce.
49
50 ::
51
52    K == [not] [B] [R0]   [R1] genrec
53      == [not] [B] [R0 [K] R1] ifte
54
55 So we just have to derive ``J``:
56
57 ::
58
59    J == R0 [K] R1
60
61 The behavior of ``J`` is to accept a (non-empty) tree node and arrive at
62 the desired outcome.
63
64 ::
65
66           [node tree*] J
67    ------------------------------
68       node N [tree*] [K] map C
69
70 So ``J`` will have some form like:
71
72 ::
73
74    J == ... [N] ... [K] ... [C] ...
75
76 Let’s dive in. First, unquote the node and ``dip`` ``N``.
77
78 ::
79
80    [node tree*] uncons [N] dip
81    node [tree*]        [N] dip
82    node N [tree*]
83
84 Next, ``map`` ``K`` over the child trees and combine with ``C``.
85
86 ::
87
88    node N [tree*] [K] map C
89    node N [tree*] [K] map C
90    node N [K.tree*]       C
91
92 So:
93
94 ::
95
96    J == uncons [N] dip [K] map C
97
98 Plug it in and convert to ``genrec``:
99
100 ::
101
102    K == [not] [B] [J                       ] ifte
103      == [not] [B] [uncons [N] dip [K] map C] ifte
104      == [not] [B] [uncons [N] dip]   [map C] genrec
105
106 Extract the givens to parameterize the program.
107 -----------------------------------------------
108
109 Working backwards:
110
111 ::
112
113    [not] [B]          [uncons [N] dip]                  [map C] genrec
114    [B] [not] swap     [uncons [N] dip]                  [map C] genrec
115    [B]                [uncons [N] dip] [[not] swap] dip [map C] genrec
116                                        ^^^^^^^^^^^^^^^^
117    [B] [[N] dip]      [uncons] swoncat [[not] swap] dip [map C] genrec
118    [B] [N] [dip] cons [uncons] swoncat [[not] swap] dip [map C] genrec
119            ^^^^^^^^^^^^^^^^^^^^^^^^^^^
120
121 Extract a couple of auxiliary definitions:
122
123 ::
124
125    TS.0 == [[not] swap] dip
126    TS.1 == [dip] cons [uncons] swoncat
127
128 ::
129
130    [B] [N] TS.1 TS.0 [map C]                         genrec
131    [B] [N]           [map C]         [TS.1 TS.0] dip genrec
132    [B] [N] [C]         [map] swoncat [TS.1 TS.0] dip genrec
133
134 The givens are all to the left so we have our definition.
135
136 (alternate) Extract the givens to parameterize the program.
137 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
138
139 Working backwards:
140
141 ::
142
143    [not] [B]           [uncons [N] dip]            [map C] genrec
144    [not] [B] [N]       [dip] cons [uncons] swoncat [map C] genrec
145    [B] [N] [not] roll> [dip] cons [uncons] swoncat [map C] genrec
146            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
147
148 Define ``treestep``
149 -------------------
150
151 .. code:: python
152
153     from notebook_preamble import D, J, V, define, DefinitionWrapper
154
155 .. code:: python
156
157     DefinitionWrapper.add_definitions('''
158     
159         _treestep_0 == [[not] swap] dip
160         _treestep_1 == [dip] cons [uncons] swoncat
161         treegrind == [_treestep_1 _treestep_0] dip genrec
162         treestep == [map] swoncat treegrind
163     
164     ''', D)
165
166 Examples
167 --------
168
169 Consider trees, the nodes of which are integers. We can find the sum of
170 all nodes in a tree with this function:
171
172 ::
173
174    sumtree == [pop 0] [] [sum +] treestep
175
176 .. code:: python
177
178     define('sumtree == [pop 0] [] [sum +] treestep')
179
180 Running this function on an empty tree value gives zero:
181
182 ::
183
184       [] [pop 0] [] [sum +] treestep
185    ------------------------------------
186               0
187
188 .. code:: python
189
190     J('[] sumtree')  # Empty tree.
191
192
193 .. parsed-literal::
194
195     0
196
197
198 Running it on a non-empty node:
199
200 ::
201
202    [n tree*]  [pop 0] [] [sum +] treestep
203    n [tree*] [[pop 0] [] [sum +] treestep] map sum +
204    n [ ... ]                                   sum +
205    n m                                             +
206    n+m
207
208 .. code:: python
209
210     J('[23] sumtree')  # No child trees.
211
212
213 .. parsed-literal::
214
215     23
216
217
218 .. code:: python
219
220     J('[23 []] sumtree')  # Child tree, empty.
221
222
223 .. parsed-literal::
224
225     23
226
227
228 .. code:: python
229
230     J('[23 [2 [4]] [3]] sumtree')  # Non-empty child trees.
231
232
233 .. parsed-literal::
234
235     32
236
237
238 .. code:: python
239
240     J('[23 [2 [8] [9]] [3] [4 []]] sumtree')  # Etc...
241
242
243 .. parsed-literal::
244
245     49
246
247
248 .. code:: python
249
250     J('[23 [2 [8] [9]] [3] [4 []]] [pop 0] [] [cons sum] treestep')  # Alternate "spelling".
251
252
253 .. parsed-literal::
254
255     49
256
257
258 .. code:: python
259
260     J('[23 [2 [8] [9]] [3] [4 []]] [] [pop 23] [cons] treestep')  # Replace each node.
261
262
263 .. parsed-literal::
264
265     [23 [23 [23] [23]] [23] [23 []]]
266
267
268 .. code:: python
269
270     J('[23 [2 [8] [9]] [3] [4 []]] [] [pop 1] [cons] treestep')
271
272
273 .. parsed-literal::
274
275     [1 [1 [1] [1]] [1] [1 []]]
276
277
278 .. code:: python
279
280     J('[23 [2 [8] [9]] [3] [4 []]] [] [pop 1] [cons] treestep sumtree')
281
282
283 .. parsed-literal::
284
285     6
286
287
288 .. code:: python
289
290     J('[23 [2 [8] [9]] [3] [4 []]] [pop 0] [pop 1] [sum +] treestep')  # Combine replace and sum into one function.
291
292
293 .. parsed-literal::
294
295     6
296
297
298 .. code:: python
299
300     J('[4 [3 [] [7]]] [pop 0] [pop 1] [sum +] treestep')  # Combine replace and sum into one function.
301
302
303 .. parsed-literal::
304
305     3
306
307
308 Redefining the Ordered Binary Tree in terms of ``treestep``.
309 ------------------------------------------------------------
310
311 ::
312
313    Tree = [] | [[key value] left right]
314
315 What kind of functions can we write for this with our ``treestep``?
316
317 The pattern for processing a non-empty node is:
318
319 ::
320
321    node N [tree*] [K] map C
322
323 Plugging in our BTree structure:
324
325 ::
326
327    [key value] N [left right] [K] map C
328
329 Traversal
330 ~~~~~~~~~
331
332 ::
333
334    [key value] first [left right] [K] map i
335    key [value]       [left right] [K] map i
336    key               [left right] [K] map i
337    key               [lkey rkey ]         i
338    key                lkey rkey
339
340 This doesn’t quite work:
341
342 .. code:: python
343
344     J('[[3 0] [[2 0] [][]] [[9 0] [[5 0] [[4 0] [][]] [[8 0] [[6 0] [] [[7 0] [][]]][]]][]]] ["B"] [first] [i] treestep')
345
346
347 .. parsed-literal::
348
349     3 'B' 'B'
350
351
352 Doesn’t work because ``map`` extracts the ``first`` item of whatever its
353 mapped function produces. We have to return a list, rather than
354 depositing our results directly on the stack.
355
356 ::
357
358    [key value] N     [left right] [K] map C
359
360    [key value] first [left right] [K] map flatten cons
361    key               [left right] [K] map flatten cons
362    key               [[lk] [rk] ]         flatten cons
363    key               [ lk   rk  ]                 cons
364                      [key  lk   rk  ]
365
366 So:
367
368 ::
369
370    [] [first] [flatten cons] treestep
371
372 .. code:: python
373
374     J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]]   [] [first] [flatten cons] treestep')
375
376
377 .. parsed-literal::
378
379     [3 2 9 5 4 8 6 7]
380
381
382 There we go.
383
384 In-order traversal
385 ~~~~~~~~~~~~~~~~~~
386
387 From here:
388
389 ::
390
391    key [[lk] [rk]] C
392    key [[lk] [rk]] i
393    key  [lk] [rk] roll<
394    [lk] [rk] key swons concat
395    [lk] [key rk]       concat
396    [lk   key rk]
397
398 So:
399
400 ::
401
402    [] [i roll< swons concat] [first] treestep
403
404 .. code:: python
405
406     J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]]   [] [uncons pop] [i roll< swons concat] treestep')
407
408
409 .. parsed-literal::
410
411     [2 3 4 5 6 7 8 9]
412
413
414 With ``treegrind``?
415 -------------------
416
417 The ``treegrind`` function doesn’t include the ``map`` combinator, so
418 the ``[C]`` function must arrange to use some combinator on the quoted
419 recursive copy ``[K]``. With this function, the pattern for processing a
420 non-empty node is:
421
422 ::
423
424    node N [tree*] [K] C
425
426 Plugging in our BTree structure:
427
428 ::
429
430    [key value] N [left right] [K] C
431
432 .. code:: python
433
434     J('[["key" "value"] ["left"] ["right"] ] ["B"] ["N"] ["C"] treegrind')
435
436
437 .. parsed-literal::
438
439     ['key' 'value'] 'N' [['left'] ['right']] [[not] ['B'] [uncons ['N'] dip] ['C'] genrec] 'C'
440
441
442 ``treegrind`` with ``step``
443 ---------------------------
444
445 Iteration through the nodes
446
447 .. code:: python
448
449     J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]]   [pop] ["N"] [step] treegrind')
450
451
452 .. parsed-literal::
453
454     [3 0] 'N' [2 0] 'N' [9 0] 'N' [5 0] 'N' [4 0] 'N' [8 0] 'N' [6 0] 'N' [7 0] 'N'
455
456
457 Sum the nodes’ keys.
458
459 .. code:: python
460
461     J('0 [[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]]   [pop] [first +] [step] treegrind')
462
463
464 .. parsed-literal::
465
466     44
467
468
469 Rebuild the tree using ``map`` (imitating ``treestep``.)
470
471 .. code:: python
472
473     J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]]   [] [[100 +] infra] [map cons] treegrind')
474
475
476 .. parsed-literal::
477
478     [[103 0] [[102 0] [] []] [[109 0] [[105 0] [[104 0] [] []] [[108 0] [[106 0] [] [[107 0] [] []]] []]] []]]
479
480
481 Do we have the flexibility to reimplement ``Tree-get``?
482 -------------------------------------------------------
483
484 I think we do:
485
486 ::
487
488    [B] [N] [C] treegrind
489
490 We’ll start by saying that the base-case (the key is not in the tree) is
491 user defined, and the per-node function is just the query key literal:
492
493 ::
494
495    [B] [query_key] [C] treegrind
496
497 This means we just have to define ``C`` from:
498
499 ::
500
501    [key value] query_key [left right] [K] C
502
503 Let’s try ``cmp``:
504
505 ::
506
507    C == P [T>] [E] [T<] cmp
508
509    [key value] query_key [left right] [K] P [T>] [E] [T<] cmp
510
511 The predicate ``P``
512 ~~~~~~~~~~~~~~~~~~~
513
514 Seems pretty easy (we must preserve the value in case the keys are
515 equal):
516
517 ::
518
519    [key value] query_key [left right] [K] P
520    [key value] query_key [left right] [K] roll<
521    [key value] [left right] [K] query_key       [roll< uncons swap] dip
522
523    [key value] [left right] [K] roll< uncons swap query_key
524    [left right] [K] [key value]       uncons swap query_key
525    [left right] [K] key [value]              swap query_key
526    [left right] [K] [value] key                   query_key
527
528    P == roll< [roll< uncons swap] dip
529
530 (Possibly with a swap at the end? Or just swap ``T<`` and ``T>``.)
531
532 So now:
533
534 ::
535
536    [left right] [K] [value] key query_key [T>] [E] [T<] cmp
537
538 Becomes one of these three:
539
540 ::
541
542    [left right] [K] [value] T>
543    [left right] [K] [value] E
544    [left right] [K] [value] T<
545
546 ``E``
547 ~~~~~
548
549 Easy.
550
551 ::
552
553    E == roll> popop first
554
555 ``T<`` and ``T>``
556 ~~~~~~~~~~~~~~~~~
557
558 ::
559
560    T< == pop [first] dip i
561    T> == pop [second] dip i
562
563 Putting it together
564 -------------------
565
566 ::
567
568    T> == pop [first] dip i
569    T< == pop [second] dip i
570    E == roll> popop first
571    P == roll< [roll< uncons swap] dip
572
573    Tree-get == [P [T>] [E] [T<] cmp] treegrind
574
575 To me, that seems simpler than the ``genrec`` version.
576
577 .. code:: python
578
579     DefinitionWrapper.add_definitions('''
580     
581         T> == pop [first] dip i
582         T< == pop [second] dip i
583         E == roll> popop first
584         P == roll< [roll< uncons swap] dip
585     
586         Tree-get == [P [T>] [E] [T<] cmp] treegrind
587     
588     ''', D)
589
590 .. code:: python
591
592     J('''\
593     
594     [[3 13] [[2 12] [] []] [[9 19] [[5 15] [[4 14] [] []] [[8 18] [[6 16] [] [[7 17] [] []]] []]] []]]
595     
596     [] [5] Tree-get
597     
598     ''')
599
600
601 .. parsed-literal::
602
603     15
604
605
606 .. code:: python
607
608     J('''\
609     
610     [[3 13] [[2 12] [] []] [[9 19] [[5 15] [[4 14] [] []] [[8 18] [[6 16] [] [[7 17] [] []]] []]] []]]
611     
612     [pop "nope"] [25] Tree-get
613     
614     ''')
615
616
617 .. parsed-literal::
618
619     'nope'
620