OSDN Git Service

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