4 "cell_type": "markdown",
7 "# Treating Trees II: `treestep`\n",
8 "Let's consider a tree structure, similar to one described [\"Why functional programming matters\" by John Hughes](https://www.cs.kent.ac.uk/people/staff/dat/miranda/whyfp90.pdf), that consists of a node value followed by zero or more child trees. (The asterisk is meant to indicate the [Kleene star](https://en.wikipedia.org/wiki/Kleene_star).)\n",
10 " tree = [] | [node tree*]"
14 "cell_type": "markdown",
17 "In the spirit of `step` we are going to define a combinator `treestep` which expects a tree and three additional items: a base-case function `[B]`, and two quoted programs `[N]` and `[C]`.\n",
19 " tree [B] [N] [C] treestep"
23 "cell_type": "markdown",
26 "If the current tree node is empty then just execute `B`:\n",
28 " [] [B] [N] [C] treestep\n",
29 " ---------------------------\n",
34 "cell_type": "markdown",
37 "Otherwise, evaluate `N` on the node value, `map` the whole function (abbreviated here as `K`) over the child trees recursively, and then combine the result with `C`.\n",
39 " [node tree*] [B] [N] [C] treestep\n",
40 " --------------------------------------- w/ K == [B] [N] [C] treestep\n",
41 " node N [tree*] [K] map C\n",
43 "(Later on we'll experiment with making `map` part of `C` so you can use other combinators.)"
47 "cell_type": "markdown",
50 "## Derive the recursive function.\n",
51 "We can begin to derive it by finding the `ifte` stage that `genrec` will produce.\n",
53 " K == [not] [B] [R0] [R1] genrec\n",
54 " == [not] [B] [R0 [K] R1] ifte\n",
56 "So we just have to derive `J`:\n",
62 "cell_type": "markdown",
65 "The behavior of `J` is to accept a (non-empty) tree node and arrive at the desired outcome.\n",
68 " ------------------------------\n",
69 " node N [tree*] [K] map C"
73 "cell_type": "markdown",
76 "So `J` will have some form like:\n",
78 " J == ... [N] ... [K] ... [C] ..."
82 "cell_type": "markdown",
85 "Let's dive in. First, unquote the node and `dip` `N`.\n",
87 " [node tree*] uncons [N] dip\n",
88 " node [tree*] [N] dip\n",
93 "cell_type": "markdown",
96 "Next, `map` `K` over the child trees and combine with `C`.\n",
98 " node N [tree*] [K] map C\n",
99 " node N [tree*] [K] map C\n",
100 " node N [K.tree*] C"
104 "cell_type": "markdown",
109 " J == uncons [N] dip [K] map C"
113 "cell_type": "markdown",
116 "Plug it in and convert to `genrec`:\n",
118 " K == [not] [B] [J ] ifte\n",
119 " == [not] [B] [uncons [N] dip [K] map C] ifte\n",
120 " == [not] [B] [uncons [N] dip] [map C] genrec"
124 "cell_type": "markdown",
127 "## Extract the givens to parameterize the program.\n",
128 "Working backwards:\n",
130 " [not] [B] [uncons [N] dip] [map C] genrec\n",
131 " [B] [not] swap [uncons [N] dip] [map C] genrec\n",
132 " [B] [uncons [N] dip] [[not] swap] dip [map C] genrec\n",
133 " ^^^^^^^^^^^^^^^^\n",
134 " [B] [[N] dip] [uncons] swoncat [[not] swap] dip [map C] genrec\n",
135 " [B] [N] [dip] cons [uncons] swoncat [[not] swap] dip [map C] genrec\n",
136 " ^^^^^^^^^^^^^^^^^^^^^^^^^^^"
140 "cell_type": "markdown",
143 "Extract a couple of auxiliary definitions:\n",
145 " TS.0 == [[not] swap] dip\n",
146 " TS.1 == [dip] cons [uncons] swoncat"
150 "cell_type": "markdown",
153 " [B] [N] TS.1 TS.0 [map C] genrec\n",
154 " [B] [N] [map C] [TS.1 TS.0] dip genrec\n",
155 " [B] [N] [C] [map] swoncat [TS.1 TS.0] dip genrec\n",
157 "The givens are all to the left so we have our definition."
161 "cell_type": "markdown",
164 "### (alternate) Extract the givens to parameterize the program.\n",
165 "Working backwards:\n",
167 " [not] [B] [uncons [N] dip] [map C] genrec\n",
168 " [not] [B] [N] [dip] cons [uncons] swoncat [map C] genrec\n",
169 " [B] [N] [not] roll> [dip] cons [uncons] swoncat [map C] genrec\n",
170 " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"
174 "cell_type": "markdown",
177 "## Define `treestep`"
182 "execution_count": 1,
186 "from notebook_preamble import D, J, V, define, DefinitionWrapper"
191 "execution_count": 2,
195 "DefinitionWrapper.add_definitions('''\n",
197 " _treestep_0 == [[not] swap] dip\n",
198 " _treestep_1 == [dip] cons [uncons] swoncat\n",
199 " treegrind == [_treestep_1 _treestep_0] dip genrec\n",
200 " treestep == [map] swoncat treegrind\n",
206 "cell_type": "markdown",
210 "Consider trees, the nodes of which are integers. We can find the sum of all nodes in a tree with this function:\n",
212 " sumtree == [pop 0] [] [sum +] treestep"
217 "execution_count": 3,
221 "define('sumtree == [pop 0] [] [sum +] treestep')"
225 "cell_type": "markdown",
228 "Running this function on an empty tree value gives zero:\n",
230 " [] [pop 0] [] [sum +] treestep\n",
231 " ------------------------------------\n",
237 "execution_count": 4,
242 "output_type": "stream",
249 "J('[] sumtree') # Empty tree."
253 "cell_type": "markdown",
256 "Running it on a non-empty node:\n",
258 " [n tree*] [pop 0] [] [sum +] treestep\n",
259 " n [tree*] [[pop 0] [] [sum +] treestep] map sum +\n",
260 " n [ ... ] sum +\n",
267 "execution_count": 5,
274 "output_type": "stream",
281 "J('[23] sumtree') # No child trees."
286 "execution_count": 6,
293 "output_type": "stream",
300 "J('[23 []] sumtree') # Child tree, empty."
305 "execution_count": 7,
310 "output_type": "stream",
317 "J('[23 [2 [4]] [3]] sumtree') # Non-empty child trees."
322 "execution_count": 8,
327 "output_type": "stream",
334 "J('[23 [2 [8] [9]] [3] [4 []]] sumtree') # Etc..."
339 "execution_count": 9,
344 "output_type": "stream",
351 "J('[23 [2 [8] [9]] [3] [4 []]] [pop 0] [] [cons sum] treestep') # Alternate \"spelling\"."
356 "execution_count": 10,
361 "output_type": "stream",
363 "[23 [23 [23] [23]] [23] [23 []]]\n"
368 "J('[23 [2 [8] [9]] [3] [4 []]] [] [pop 23] [cons] treestep') # Replace each node."
373 "execution_count": 11,
378 "output_type": "stream",
380 "[1 [1 [1] [1]] [1] [1 []]]\n"
385 "J('[23 [2 [8] [9]] [3] [4 []]] [] [pop 1] [cons] treestep')"
390 "execution_count": 12,
395 "output_type": "stream",
402 "J('[23 [2 [8] [9]] [3] [4 []]] [] [pop 1] [cons] treestep sumtree')"
407 "execution_count": 13,
414 "output_type": "stream",
421 "J('[23 [2 [8] [9]] [3] [4 []]] [pop 0] [pop 1] [sum +] treestep') # Combine replace and sum into one function."
426 "execution_count": 14,
433 "output_type": "stream",
440 "J('[4 [3 [] [7]]] [pop 0] [pop 1] [sum +] treestep') # Combine replace and sum into one function."
444 "cell_type": "markdown",
447 "## Redefining the Ordered Binary Tree in terms of `treestep`.\n",
449 " Tree = [] | [[key value] left right]"
453 "cell_type": "markdown",
456 "What kind of functions can we write for this with our `treestep`?\n",
458 "The pattern for processing a non-empty node is:\n",
460 " node N [tree*] [K] map C\n",
462 "Plugging in our BTree structure:\n",
464 " [key value] N [left right] [K] map C"
468 "cell_type": "markdown",
472 " [key value] first [left right] [K] map i\n",
473 " key [value] [left right] [K] map i\n",
474 " key [left right] [K] map i\n",
475 " key [lkey rkey ] i\n",
480 "cell_type": "markdown",
483 "This doesn't quite work:"
488 "execution_count": 15,
493 "output_type": "stream",
500 "J('[[3 0] [[2 0] [][]] [[9 0] [[5 0] [[4 0] [][]] [[8 0] [[6 0] [] [[7 0] [][]]][]]][]]] [\"B\"] [first] [i] treestep')"
504 "cell_type": "markdown",
507 "Doesn't work because `map` extracts the `first` item of whatever its mapped function produces. We have to return a list, rather than depositing our results directly on the stack.\n",
510 " [key value] N [left right] [K] map C\n",
512 " [key value] first [left right] [K] map flatten cons\n",
513 " key [left right] [K] map flatten cons\n",
514 " key [[lk] [rk] ] flatten cons\n",
515 " key [ lk rk ] cons\n",
520 " [] [first] [flatten cons] treestep"
525 "execution_count": 16,
532 "output_type": "stream",
534 "[3 2 9 5 4 8 6 7]\n"
539 "J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]] [] [first] [flatten cons] treestep')"
543 "cell_type": "markdown",
550 "cell_type": "markdown",
553 "### In-order traversal\n",
557 " key [[lk] [rk]] C\n",
558 " key [[lk] [rk]] i\n",
559 " key [lk] [rk] roll<\n",
560 " [lk] [rk] key swons concat\n",
561 " [lk] [key rk] concat\n",
566 " [] [i roll< swons concat] [first] treestep"
571 "execution_count": 17,
576 "output_type": "stream",
578 "[2 3 4 5 6 7 8 9]\n"
583 "J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]] [] [uncons pop] [i roll< swons concat] treestep')"
587 "cell_type": "markdown",
590 "## With `treegrind`?\n",
591 "The `treegrind` function doesn't include the `map` combinator, so the `[C]` function must arrange to use some combinator on the quoted recursive copy `[K]`. With this function, the pattern for processing a non-empty node is:\n",
593 " node N [tree*] [K] C\n",
595 "Plugging in our BTree structure:\n",
597 " [key value] N [left right] [K] C"
602 "execution_count": 18,
607 "output_type": "stream",
609 "['key' 'value'] 'N' [['left'] ['right']] [[not] ['B'] [uncons ['N'] dip] ['C'] genrec] 'C'\n"
614 "J('[[\"key\" \"value\"] [\"left\"] [\"right\"] ] [\"B\"] [\"N\"] [\"C\"] treegrind')"
618 "cell_type": "markdown",
621 "## `treegrind` with `step`"
625 "cell_type": "markdown",
628 "Iteration through the nodes"
633 "execution_count": 19,
638 "output_type": "stream",
640 "[3 0] 'N' [2 0] 'N' [9 0] 'N' [5 0] 'N' [4 0] 'N' [8 0] 'N' [6 0] 'N' [7 0] 'N'\n"
645 "J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]] [pop] [\"N\"] [step] treegrind')"
649 "cell_type": "markdown",
652 "Sum the nodes' keys."
657 "execution_count": 20,
662 "output_type": "stream",
669 "J('0 [[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]] [pop] [first +] [step] treegrind')"
673 "cell_type": "markdown",
676 "Rebuild the tree using `map` (imitating `treestep`.)"
681 "execution_count": 21,
686 "output_type": "stream",
688 "[[103 0] [[102 0] [] []] [[109 0] [[105 0] [[104 0] [] []] [[108 0] [[106 0] [] [[107 0] [] []]] []]] []]]\n"
693 "J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]] [] [[100 +] infra] [map cons] treegrind')"
697 "cell_type": "markdown",
700 "## Do we have the flexibility to reimplement `Tree-get`?\n",
703 " [B] [N] [C] treegrind"
707 "cell_type": "markdown",
710 "We'll start by saying that the base-case (the key is not in the tree) is user defined, and the per-node function is just the query key literal:\n",
712 " [B] [query_key] [C] treegrind"
716 "cell_type": "markdown",
719 "This means we just have to define `C` from:\n",
721 " [key value] query_key [left right] [K] C\n",
726 "cell_type": "markdown",
729 "Let's try `cmp`:\n",
731 " C == P [T>] [E] [T<] cmp\n",
733 " [key value] query_key [left right] [K] P [T>] [E] [T<] cmp"
737 "cell_type": "markdown",
740 "### The predicate `P`\n",
741 "Seems pretty easy (we must preserve the value in case the keys are equal):\n",
743 " [key value] query_key [left right] [K] P\n",
744 " [key value] query_key [left right] [K] roll<\n",
745 " [key value] [left right] [K] query_key [roll< uncons swap] dip\n",
747 " [key value] [left right] [K] roll< uncons swap query_key\n",
748 " [left right] [K] [key value] uncons swap query_key\n",
749 " [left right] [K] key [value] swap query_key\n",
750 " [left right] [K] [value] key query_key\n",
752 " P == roll< [roll< uncons swap] dip\n",
754 "(Possibly with a swap at the end? Or just swap `T<` and `T>`.)"
758 "cell_type": "markdown",
763 " [left right] [K] [value] key query_key [T>] [E] [T<] cmp\n",
765 "Becomes one of these three:\n",
767 " [left right] [K] [value] T>\n",
768 " [left right] [K] [value] E\n",
769 " [left right] [K] [value] T<\n"
773 "cell_type": "markdown",
779 " E == roll> popop first"
783 "cell_type": "markdown",
786 "### `T<` and `T>`\n",
788 " T< == pop [first] dip i\n",
789 " T> == pop [second] dip i"
793 "cell_type": "markdown",
796 "## Putting it together\n",
799 " T> == pop [first] dip i\n",
800 " T< == pop [second] dip i\n",
801 " E == roll> popop first\n",
802 " P == roll< [roll< uncons swap] dip\n",
804 " Tree-get == [P [T>] [E] [T<] cmp] treegrind\n",
806 "To me, that seems simpler than the `genrec` version."
811 "execution_count": 22,
815 "DefinitionWrapper.add_definitions('''\n",
817 " T> == pop [first] dip i\n",
818 " T< == pop [second] dip i\n",
819 " E == roll> popop first\n",
820 " P == roll< [roll< uncons swap] dip\n",
822 " Tree-get == [P [T>] [E] [T<] cmp] treegrind\n",
829 "execution_count": 23,
834 "output_type": "stream",
843 "[[3 13] [[2 12] [] []] [[9 19] [[5 15] [[4 14] [] []] [[8 18] [[6 16] [] [[7 17] [] []]] []]] []]]\n",
852 "execution_count": 24,
857 "output_type": "stream",
866 "[[3 13] [[2 12] [] []] [[9 19] [[5 15] [[4 14] [] []] [[8 18] [[6 16] [] [[7 17] [] []]] []]] []]]\n",
868 "[pop \"nope\"] [25] Tree-get\n",
876 "display_name": "Python 2",
877 "language": "python",
885 "file_extension": ".py",
886 "mimetype": "text/x-python",
888 "nbconvert_exporter": "python",
889 "pygments_lexer": "ipython2",