1 # Treating Trees I: Ordered Binary Trees
3 Although any expression in Joy can be considered to describe a [tree](https://en.wikipedia.org/wiki/Tree_structure) with the quotes as compound nodes and the non-quote values as leaf nodes, in this page I want to talk about [ordered binary trees](https://en.wikipedia.org/wiki/Binary_search_tree) and how to make and use them.
5 The basic structure, in a [crude type notation](https://en.wikipedia.org/wiki/Algebraic_data_type), is:
7 Tree :: [] | [key value Tree Tree]
9 That says that a Tree is either the empty quote `[]` or a quote with four items: a key, a value, and two Trees representing the left and right branches of the tree.
11 We're going to derive some recursive functions to work with such datastructures:
19 Once these functions are defined we have a new "type" to work with, and the Sufficiently Smart Compiler can be modified to use an optimized implementation under the hood. (Where does the "type" come from? It has a contingent existence predicated on the disciplined use of these functions on otherwise undistinguished Joy datastructures.)
23 from notebook_preamble import D, J, V, define, DefinitionWrapper
26 ## Adding Nodes to the Tree
27 Let's consider adding nodes to a Tree structure.
29 Tree value key Tree-add
30 -----------------------------
33 ### Adding to an empty node.
34 If the current node is `[]` then you just return `[key value [] []]`:
36 Tree-add == [popop not] [[pop] dipd Tree-new] [R0] [R1] genrec
39 Where `Tree-new` is defined as:
42 ------------------------
47 value key swap [[] []] cons cons
48 key value [[] []] cons cons
49 key [value [] []] cons
54 Tree-new == swap [[] []] cons cons
58 define('Tree-new == swap [[] []] cons cons')
69 (As an implementation detail, the `[[] []]` literal used in the definition of `Tree-new` will be reused to supply the *constant* tail for *all* new nodes produced by it. This is one of those cases where you get amortized storage "for free" by using [persistent datastructures](https://en.wikipedia.org/wiki/Persistent_data_structure). Because the tail, which is `((), ((), ()))` in Python, is immutable and embedded in the definition body for `Tree-new`, all new nodes can reuse it as their own tail without fear that some other code somewhere will change it.)
71 ### Adding to a non-empty node.
73 We now have to derive `R0` and `R1`, consider:
75 [key_n value_n left right] value key R0 [Tree-add] R1
77 In this case, there are three possibilites: the key can be greater or less than or equal to the node's key. In two of those cases we will need to apply a copy of `Tree-add`, so `R0` is pretty much out of the picture.
81 #### A predicate to compare keys.
83 [key_n value_n left right] value key [BTree-add] R1
85 The first thing we need to do is compare the the key we're adding to the node key and `branch` accordingly:
87 [key_n value_n left right] value key [BTree-add] [P] [T] [E] ifte
89 That would suggest something like:
91 [key_n value_n left right] value key [BTree-add] P
92 [key_n value_n left right] value key [BTree-add] pop roll> pop first >
93 [key_n value_n left right] value key roll> pop first >
94 key [key_n value_n left right] value roll> pop first >
98 Let's abstract the predicate just a little to let us specify the comparison operator:
100 P > == pop roll> pop first >
101 P < == pop roll> pop first <
102 P == pop roll> pop first
106 define('P == pop roll> pop first')
111 J('["old_key" 23 [] []] 17 "new_key" ["..."] P')
117 #### If the key we're adding is greater than the node's key.
119 Here the parentheses are meant to signify that the expression is not literal, the code in the parentheses is meant to have been evaluated:
121 [key_n value_n left right] value key [Tree-add] T
122 -------------------------------------------------------
123 [key_n value_n left (Tree-add key value right)]
125 So how do we do this? We're going to want to use `infra` on some function `K` that has the key and value to work with, as well as the quoted copy of `Tree-add` to apply somehow. Considering the node as a stack:
127 right left value_n key_n value key [Tree-add] K
128 -----------------------------------------------------
129 right value key Tree-add left value_n key_n
133 right left value_n key_n value key [Tree-add] cons cons dipdd
134 right left value_n key_n [value key Tree-add] dipdd
135 right value key Tree-add left value_n key_n
141 Looking at it from the point-of-view of the node as node again:
143 [key_n value_n left right] [value key [Tree-add] K] infra
145 Expand `K` and evaluate a little:
147 [key_n value_n left right] [value key [Tree-add] K] infra
148 [key_n value_n left right] [value key [Tree-add] cons cons dipdd] infra
149 [key_n value_n left right] [[value key Tree-add] dipdd] infra
151 Then, working backwards:
153 [key_n value_n left right] [[value key Tree-add] dipdd] infra
154 [key_n value_n left right] [value key Tree-add] [dipdd] cons infra
155 [key_n value_n left right] value key [Tree-add] cons cons [dipdd] cons infra
160 T == cons cons [dipdd] cons infra
164 define('T == cons cons [dipdd] cons infra')
169 J('["old_k" "old_value" "left" "right"] "new_value" "new_key" ["Tree-add"] T')
172 ['old_k' 'old_value' 'left' 'Tree-add' 'new_key' 'new_value' 'right']
175 #### If the key we're adding is less than the node's key.
176 This is very very similar to the above:
178 [key_n value_n left right] value key [Tree-add] E
179 [key_n value_n left right] value key [Tree-add] [P <] [Te] [Ee] ifte
183 define('E == [P <] [Te] [Ee] ifte')
186 In this case `Te` works that same as `T` but on the left child tree instead of the right, so the only difference is that it must use `dipd` instead of `dipdd`:
188 Te == cons cons [dipd] cons infra
192 define('Te == cons cons [dipd] cons infra')
197 J('["old_k" "old_value" "left" "right"] "new_value" "new_key" ["Tree-add"] Te')
200 ['old_k' 'old_value' 'Tree-add' 'new_key' 'new_value' 'left' 'right']
203 #### Else the keys must be equal.
204 This means we must find:
206 [key old_value left right] new_value key [Tree-add] Ee
207 ------------------------------------------------------------
208 [key new_value left right]
210 This is another easy one:
212 Ee == pop swap roll< rest rest cons cons
216 [key old_value left right] new_value key [Tree-add] pop swap roll< rest rest cons cons
217 [key old_value left right] new_value key swap roll< rest rest cons cons
218 [key old_value left right] key new_value roll< rest rest cons cons
219 key new_value [key old_value left right] rest rest cons cons
220 key new_value [ left right] cons cons
221 [key new_value left right]
225 define('Ee == pop swap roll< rest rest cons cons')
230 J('["k" "old_value" "left" "right"] "new_value" "k" ["Tree-add"] Ee')
233 ['k' 'new_value' 'left' 'right']
236 ### Now we can define `Tree-add`
237 Tree-add == [popop not] [[pop] dipd Tree-new] [] [[P >] [T] [E] ifte] genrec
239 Putting it all together:
241 Tree-new == swap [[] []] cons cons
242 P == pop roll> pop first
243 T == cons cons [dipdd] cons infra
244 Te == cons cons [dipd] cons infra
245 Ee == pop swap roll< rest rest cons cons
246 E == [P <] [Te] [Ee] ifte
247 R == [P >] [T] [E] ifte
249 Tree-add == [popop not] [[pop] dipd Tree-new] [] [R] genrec
253 define('Tree-add == [popop not] [[pop] dipd Tree-new] [] [[P >] [T] [E] ifte] genrec')
260 J('[] 23 "b" Tree-add') # Initial
268 J('["b" 23 [] []] 88 "c" Tree-add') # Greater than
271 ['b' 23 [] ['c' 88 [] []]]
276 J('["b" 23 [] []] 88 "a" Tree-add') # Less than
279 ['b' 23 ['a' 88 [] []] []]
284 J('["b" 23 [] []] 88 "b" Tree-add') # Equal to
292 J('[] 23 "b" Tree-add 88 "a" Tree-add 44 "c" Tree-add') # Series.
295 ['b' 23 ['a' 88 [] []] ['c' 44 [] []]]
300 J('[] [[23 "b"] [88 "a"] [44 "c"]] [i Tree-add] step')
303 ['b' 23 ['a' 88 [] []] ['c' 44 [] []]]
306 ## Interlude: `cmp` combinator
307 Instead of mucking about with nested `ifte` combinators let's use `cmp` which takes two values and three quoted programs on the stack and runs one of the three depending on the results of comparing the two values:
310 ------------------------- a > b
314 ------------------------- a = b
318 ------------------------- a < b
323 J("1 0 ['G'] ['E'] ['L'] cmp")
331 J("1 1 ['G'] ['E'] ['L'] cmp")
339 J("0 1 ['G'] ['E'] ['L'] cmp")
345 ### Redefine `Tree-add`
346 We need a new non-destructive predicate `P`:
348 [node_key node_value left right] value key [Tree-add] P
349 ------------------------------------------------------------------------
350 [node_key node_value left right] value key [Tree-add] key node_key
352 Let's start with `over` to get a copy of the key and then apply some function `Q` with the `nullary` combinator so it can dig out the node key (by throwing everything else away):
354 P == over [Q] nullary
356 [node_key node_value left right] value key [Tree-add] over [Q] nullary
357 [node_key node_value left right] value key [Tree-add] key [Q] nullary
361 Q == popop popop first
363 [node_key node_value left right] value key [Tree-add] key Q
364 [node_key node_value left right] value key [Tree-add] key popop popop first
365 [node_key node_value left right] value key popop first
366 [node_key node_value left right] first
371 P == over [popop popop first] nullary
375 define('P == over [popop popop first] nullary')
378 Using `cmp` to simplify [our code above at `R1`](#Adding-to-a-non-empty-node.):
380 [node_key node_value left right] value key [Tree-add] R1
381 [node_key node_value left right] value key [Tree-add] P [T] [E] [Te] cmp
383 The line above becomes one of the three lines below:
385 [node_key node_value left right] value key [Tree-add] T
386 [node_key node_value left right] value key [Tree-add] E
387 [node_key node_value left right] value key [Tree-add] Te
389 The definition is a little longer but, I think, more elegant and easier to understand:
391 Tree-add == [popop not] [[pop] dipd Tree-new] [] [P [T] [Ee] [Te] cmp] genrec
395 define('Tree-add == [popop not] [[pop] dipd Tree-new] [] [P [T] [Ee] [Te] cmp] genrec')
400 J('[] 23 "b" Tree-add 88 "a" Tree-add 44 "c" Tree-add') # Still works.
403 ['b' 23 ['a' 88 [] []] ['c' 44 [] []]]
406 ## A Function to Traverse this Structure
407 Let's take a crack at writing a function that can recursively iterate or traverse these trees.
410 The stopping predicate just has to detect the empty list:
412 Tree-iter == [not] [E] [R0] [R1] genrec
414 And since there's nothing at this node, we just `pop` it:
416 Tree-iter == [not] [pop] [R0] [R1] genrec
418 ### Node case `[key value left right]`
419 Now we need to figure out `R0` and `R1`:
421 Tree-iter == [not] [pop] [R0] [R1] genrec
422 == [not] [pop] [R0 [Tree-iter] R1] ifte
424 Let's look at it *in situ*:
426 [key value left right] R0 [Tree-iter] R1
428 #### Processing the current node.
430 `R0` is almost certainly going to use `dup` to make a copy of the node and then `dip` on some function to process the copy with it:
432 [key value left right] [F] dupdip [Tree-iter] R1
433 [key value left right] F [key value left right] [Tree-iter] R1
435 For example, if we're getting all the keys `F` would be `first`:
439 [key value left right] [first] dupdip [Tree-iter] R1
440 [key value left right] first [key value left right] [Tree-iter] R1
441 key [key value left right] [Tree-iter] R1
444 Now `R1` needs to apply `[Tree-iter]` to `left` and `right`. If we drop the key and value from the node using `rest` twice we are left with an interesting situation:
446 key [key value left right] [Tree-iter] R1
447 key [key value left right] [Tree-iter] [rest rest] dip
448 key [key value left right] rest rest [Tree-iter]
449 key [left right] [Tree-iter]
453 key [left right] [Tree-iter] step
454 key left Tree-iter [right] [Tree-iter] step
455 key left-keys [right] [Tree-iter] step
456 key left-keys right Tree-iter
457 key left-keys right-keys
461 R1 == [rest rest] dip step
463 ### Putting it together
466 Tree-iter == [not] [pop] [[F] dupdip] [[rest rest] dip step] genrec
468 When I was reading this over I realized `rest rest` could go in `R0`:
470 Tree-iter == [not] [pop] [[F] dupdip rest rest] [step] genrec
472 (And `[step] genrec` is such a cool and suggestive combinator!)
474 ### Parameterizing the `F` per-node processing function.
477 ------------------------------------------------------
478 [not] [pop] [[F] dupdip rest rest] [step] genrec
482 [not] [pop] [[F] dupdip rest rest] [step] genrec
483 [not] [pop] [F] [dupdip rest rest] cons [step] genrec
484 [F] [not] [pop] roll< [dupdip rest rest] cons [step] genrec
488 Tree-iter == [not] [pop] roll< [dupdip rest rest] cons [step] genrec
492 define('Tree-iter == [not] [pop] roll< [dupdip rest rest] cons [step] genrec')
499 J('[] [foo] Tree-iter') # It doesn't matter what F is as it won't be used.
507 J("['b' 23 ['a' 88 [] []] ['c' 44 [] []]] [first] Tree-iter")
515 J("['b' 23 ['a' 88 [] []] ['c' 44 [] []]] [second] Tree-iter")
521 ## Interlude: A Set-like Datastructure
522 We can use this to make a set-like datastructure by just setting values to e.g. 0 and ignoring them. It's set-like in that duplicate items added to it will only occur once within it, and we can query it in [$O(\log_2 N)$](https://en.wikipedia.org/wiki/Binary_search_tree#cite_note-2) time.
526 J('[] [3 9 5 2 8 6 7 8 4] [0 swap Tree-add] step')
529 [3 0 [2 0 [] []] [9 0 [5 0 [4 0 [] []] [8 0 [6 0 [] [7 0 [] []]] []]] []]]
534 define('to_set == [] swap [0 swap Tree-add] step')
539 J('[3 9 5 2 8 6 7 8 4] to_set')
542 [3 0 [2 0 [] []] [9 0 [5 0 [4 0 [] []] [8 0 [6 0 [] [7 0 [] []]] []]] []]]
545 And with that we can write a little program `unique` to remove duplicate items from a list.
549 define('unique == [to_set [first] Tree-iter] cons run')
554 J('[3 9 3 5 2 9 8 8 8 6 2 7 8 4 3] unique') # Filter duplicate items.
560 ## A Version of `Tree-iter` that does In-Order Traversal
562 If you look back to the [non-empty case of the `Tree-iter` function](#Node-case-[key-value-left-right]) we can design a variant that first processes the left child, then the current node, then the right child. This will allow us to traverse the tree in sort order.
564 Tree-iter-order == [not] [pop] [R0] [R1] genrec
566 To define `R0` and `R1` it helps to look at them as they will appear when they run:
568 [key value left right] R0 [BTree-iter-order] R1
570 ### Process the left child.
571 Staring at this for a bit suggests `dup third` to start:
573 [key value left right] R0 [Tree-iter-order] R1
574 [key value left right] dup third [Tree-iter-order] R1
575 [key value left right] left [Tree-iter-order] R1
579 [key value left right] left [Tree-iter-order] [cons dip] dupdip
580 [key value left right] left [Tree-iter-order] cons dip [Tree-iter-order]
581 [key value left right] [left Tree-iter-order] dip [Tree-iter-order]
582 left Tree-iter-order [key value left right] [Tree-iter-order]
584 ### Process the current node.
585 So far, so good. Now we need to process the current node's values:
587 left Tree-iter-order [key value left right] [Tree-iter-order] [[F] dupdip] dip
588 left Tree-iter-order [key value left right] [F] dupdip [Tree-iter-order]
589 left Tree-iter-order [key value left right] F [key value left right] [Tree-iter-order]
591 If `F` needs items from the stack below the left stuff it should have `cons`'d them before beginning maybe? For functions like `first` it works fine as-is.
593 left Tree-iter-order [key value left right] first [key value left right] [Tree-iter-order]
594 left Tree-iter-order key [key value left right] [Tree-iter-order]
596 ### Process the right child.
597 First ditch the rest of the node and get the right child:
599 left Tree-iter-order key [key value left right] [Tree-iter-order] [rest rest rest first] dip
600 left Tree-iter-order key right [Tree-iter-order]
602 Then, of course, we just need `i` to run `Tree-iter-order` on the right side:
604 left Tree-iter-order key right [Tree-iter-order] i
605 left Tree-iter-order key right Tree-iter-order
607 ### Defining `Tree-iter-order`
608 The result is a little awkward:
610 R1 == [cons dip] dupdip [[F] dupdip] dip [rest rest rest first] dip i
612 Let's do a little semantic factoring:
614 fourth == rest rest rest first
616 proc_left == [cons dip] dupdip
617 proc_current == [[F] dupdip] dip
618 proc_right == [fourth] dip i
620 Tree-iter-order == [not] [pop] [dup third] [proc_left proc_current proc_right] genrec
622 Now we can sort sequences.
626 #define('Tree-iter-order == [not] [pop] [dup third] [[cons dip] dupdip [[first] dupdip] dip [rest rest rest first] dip i] genrec')
629 DefinitionWrapper.add_definitions('''
631 fourth == rest rest rest first
633 proc_left == [cons dip] dupdip
634 proc_current == [[first] dupdip] dip
635 proc_right == [fourth] dip i
637 Tree-iter-order == [not] [pop] [dup third] [proc_left proc_current proc_right] genrec
647 J('[3 9 5 2 8 6 7 8 4] to_set Tree-iter-order')
653 Parameterizing the `[F]` function is left as an exercise for the reader.
655 ## Getting values by key
656 Let's derive a function that accepts a tree and a key and returns the value associated with that key.
659 -----------------------
662 But what do we do if the key isn't in the tree? In Python we might raise a `KeyError` but I'd like to avoid exceptions in Joy if possible, and here I think it's possible. (Division by zero is an example of where I think it's probably better to let Python crash Joy. Sometimes the machinery fails and you have to "stop the line", I think.)
664 Let's pass the buck to the caller by making the base case a given, you have to decide for yourself what `[E]` should be.
667 tree key [E] Tree-get
668 ---------------------------- key in tree
671 tree key [E] Tree-get
672 ---------------------------- key not in tree
675 ### The base case `[]`
676 As before, the stopping predicate just has to detect the empty list:
678 Tree-get == [pop not] [E] [R0] [R1] genrec
682 Tree-get == [pop not] swap [R0] [R1] genrec
684 Note that this `Tree-get` creates a slightly different function than itself and *that function* does the actual recursion. This kind of higher-level programming is unusual in most languages but natural in Joy.
686 tree key [E] [pop not] swap [R0] [R1] genrec
687 tree key [pop not] [E] [R0] [R1] genrec
689 The anonymous specialized recursive function that will do the real work.
691 [pop not] [E] [R0] [R1] genrec
693 ### Node case `[key value left right]`
694 Now we need to figure out `R0` and `R1`:
696 [key value left right] key R0 [BTree-get] R1
698 We want to compare the search key with the key in the node, and if they are the same return the value, otherwise recur on one of the child nodes. So it's very similar to the above funtion, with `[R0] == []` and `R1 == P [T>] [E] [T<] cmp`:
700 [key value left right] key [BTree-get] P [T>] [E] [T<] cmp
704 P == over [get-node-key] nullary
705 get-node-key == pop popop first
707 The only difference is that `get-node-key` does one less `pop` because there's no value to discard.
710 Now we have to derive the branches:
712 [key_n value_n left right] key [BTree-get] T>
713 [key_n value_n left right] key [BTree-get] E
714 [key_n value_n left right] key [BTree-get] T<
716 #### Greater than and less than
717 The cases of `T>` and `T<` are similar to above but instead of using `infra` we have to discard the rest of the structure:
719 [key_n value_n left right] key [BTree-get] T>
720 ---------------------------------------------------
725 [key_n value_n left right] key [BTree-get] T<
726 ---------------------------------------------------
731 T> == [fourth] dipd i
736 [key_n value_n left right] key [BTree-get] [fourth] dipd i
737 [key_n value_n left right] fourth key [BTree-get] i
738 right key [BTree-get] i
742 Return the node's value:
744 [key_n value_n left right] key [BTree-get] E == value_n
751 fourth == rest rest rest first
752 get-node-key == pop popop first
753 P == over [get-node-key] nullary
754 T> == [fourth] dipd i
758 Tree-get == [pop not] swap [] [P [T>] [E] [T<] cmp] genrec
762 # I don't want to deal with name conflicts with the above so I'm inlining everything here.
763 # The original Joy system has "hide" which is a meta-command which allows you to use named
764 # definitions that are only in scope for a given definition. I don't want to implement
769 Tree-get == [pop not] swap [] [
770 over [pop popop first] nullary
781 J('["gary" 23 [] []] "mike" [popd " not in tree" +] Tree-get')
789 J('["gary" 23 [] []] "gary" [popop "err"] Tree-get')
799 [] [[0 'a'] [1 'b'] [2 'c']] [i Tree-add] step
801 'c' [popop 'not found'] Tree-get
813 [] [[0 'a'] [1 'b'] [2 'c']] [i Tree-add] step
815 'd' [popop 'not found'] Tree-get
825 Now let's write a function that can return a tree datastructure with a key, value pair deleted:
828 ---------------------------
831 If the key is not in tree it just returns the tree unchanged.
836 Tree-Delete == [pop not] [pop] [R0] [R1] genrec
839 Now we get to figure out the recursive case. We need the node's key to compare and we need to carry the key into recursive branches. Let `D` be shorthand for `Tree-Delete`:
841 D == Tree-Delete == [pop not] [pop] [R0] [R1] genrec
843 [node_key node_value left right] key R0 [D] R1
844 [node_key node_value left right] key over first swap dup [D] cons R1′
845 [node_key node_value left right] key [...] first swap dup [D] cons R1′
846 [node_key node_value left right] key node_key swap dup [D] cons R1′
847 [node_key node_value left right] node_key key dup [D] cons R1′
848 [node_key node_value left right] node_key key key [D] cons R1′
849 [node_key node_value left right] node_key key [key D] R1′
853 [node_key node_value left right] node_key key [key D] R1′
854 [node_key node_value left right] node_key key [key D] roll> [T>] [E] [T<] cmp
855 [node_key node_value left right] node_key key [key D] roll> [T>] [E] [T<] cmp
856 [node_key node_value left right] [key D] node_key key [T>] [E] [T<] cmp
860 R0 == over first swap dup
861 R1 == cons roll> [T>] [E] [T<] cmp
866 [node_key node_value left right] [key D] node_key key [T>] [E] [T<] cmp
868 Then becomes one of these three:
870 [node_key node_value left right] [key D] T>
871 [node_key node_value left right] [key D] E
872 [node_key node_value left right] [key D] T<
874 ### Greater than case and less than case
876 [node_key node_value left right] [F] T>
877 -------------------------------------------------
878 [node_key node_value (left F) right]
881 [node_key node_value left right] [F] T<
882 -------------------------------------------------
883 [node_key node_value left (right F)]
885 First, treating the node as a stack:
887 right left node_value node_key [key D] dipd
888 right left key D node_value node_key
889 right left' node_value node_key
893 [node_key node_value left right] [key D] [dipd] cons infra
897 T> == [dipd] cons infra
898 T< == [dipdd] cons infra
901 We have found the node in the tree where `key` equals `node_key`. We need to replace the current node with something
903 [node_key node_value left right] [key D] E
904 ------------------------------------------------
907 We have to handle three cases, so let's use `cond`.
909 #### One or more child nodes are `[]`
910 The first two cases are symmetrical: if we only have one non-empty child node return it. If both child nodes are empty return an empty node.
913 [[pop third not] pop fourth]
914 [[pop fourth not] pop third]
918 #### Both child nodes are non-empty.
919 If both child nodes are non-empty, we find the highest node in our lower sub-tree, take its key and value to replace (delete) our own, then get rid of it by recursively calling delete() on our lower sub-node with our new key.
921 (We could also find the lowest node in our higher sub-tree and take its key and value and delete it. I only implemented one of these two symmetrical options. Over a lot of deletions this might make the tree more unbalanced. Oh well.)
923 The initial structure of the default function:
925 default == [E′] cons infra
927 [node_key node_value left right] [key D] default
928 [node_key node_value left right] [key D] [E′] cons infra
929 [node_key node_value left right] [[key D] E′] infra
931 right left node_value node_key [key D] E′
933 First things first, we no longer need this node's key and value:
935 right left node_value node_key [key D] roll> popop E″
936 right left [key D] node_value node_key popop E″
937 right left [key D] E″
939 #### We have to we find the highest (right-most) node in our lower (left) sub-tree:
941 right left [key D] E″
945 right left [key D] rest E‴
948 Find the right-most node:
950 right left [D] [dup W] dip E⁗
951 right left dup W [D] E⁗
952 right left left W [D] E⁗
958 We know left is not empty:
960 [L_key L_value L_left L_right] W
962 We want to keep extracting the right node as long as it is not empty:
964 W.rightmost == [P] [B] while
970 [L_key L_value L_left L_right] P
971 [L_key L_value L_left L_right] fourth
974 This can run on `[]` so must be guarded:
976 ?fourth == [] [fourth] [] ifte
979 if_not_empty == [] swap [] ifte
980 ?fourth == [fourth] if_not_empty
983 The body is just `fourth`:
985 left [?fourth] [fourth] while W′
990 W.rightmost == [?fourth] [fourth] while
992 #### Found right-most node in our left sub-tree
993 We know rightest is not empty:
995 [R_key R_value R_left R_right] W′
996 [R_key R_value R_left R_right] W′
997 [R_key R_value R_left R_right] uncons uncons pop
998 R_key [R_value R_left R_right] uncons pop
999 R_key R_value [R_left R_right] pop
1005 W == [?fourth] [fourth] while uncons uncons pop
1009 right left left W [D] E⁗
1010 right left R_key R_value [D] E⁗
1012 #### Replace current node key and value, recursively delete rightmost
1013 Final stretch. We want to end up with something like:
1015 right left [R_key D] i R_value R_key
1016 right left R_key D R_value R_key
1017 right left′ R_value R_key
1019 If we adjust our definition of `W` to include `over` at the end:
1021 W == [fourth] [fourth] while uncons uncons pop over
1025 right left R_key R_value R_key [D] E⁗
1027 right left R_key R_value R_key [D] cons dipd E⁗′
1028 right left R_key R_value [R_key D] dipd E⁗′
1029 right left R_key D R_key R_value E⁗′
1030 right left′ R_key R_value E⁗′
1031 right left′ R_key R_value swap
1032 right left′ R_value R_key
1036 E′ == roll> popop E″
1040 E‴ == [dup W] dip E⁗
1042 E⁗ == cons dipdd swap
1046 W == [fourth] [fourth] while uncons uncons pop over
1047 E′ == roll> popop rest [dup W] dip cons dipd swap
1049 [[pop third not] pop fourth]
1050 [[pop fourth not] pop third]
1054 Minor rearrangement, move `dup` into `W`:
1056 W == dup [fourth] [fourth] while uncons uncons pop over
1057 E′ == roll> popop rest [W] dip cons dipd swap
1059 [[pop third not] pop fourth]
1060 [[pop fourth not] pop third]
1066 W.rightmost == [fourth] [fourth] while
1067 W.unpack == uncons uncons pop
1068 W == dup W.rightmost W.unpack over
1069 E.clear_stuff == roll> popop rest
1070 E.delete == cons dipd
1071 E.0 == E.clear_stuff [W] dip E.delete swap
1073 [[pop third not] pop fourth]
1074 [[pop fourth not] pop third]
1077 T> == [dipd] cons infra
1078 T< == [dipdd] cons infra
1079 R0 == over first swap dup
1080 R1 == cons roll> [T>] [E] [T<] cmp
1081 BTree-Delete == [pop not] swap [R0] [R1] genrec
1083 By the standards of the code I've written so far, this is a *huge* Joy program.
1087 DefinitionWrapper.add_definitions('''
1088 first_two == uncons uncons pop
1089 fourth == rest rest rest first
1090 ?fourth == [] [fourth] [] ifte
1091 W.rightmost == [?fourth] [fourth] while
1092 E.clear_stuff == roll> popop rest
1093 E.delete == cons dipd
1094 W == dup W.rightmost first_two over
1095 E.0 == E.clear_stuff [W] dip E.delete swap
1096 E == [[[pop third not] pop fourth] [[pop fourth not] pop third] [[E.0] cons infra]] cond
1097 T> == [dipd] cons infra
1098 T< == [dipdd] cons infra
1099 R0 == over first swap dup
1100 R1 == cons roll> [T>] [E] [T<] cmp
1101 Tree-Delete == [pop not] [pop] [R0] [R1] genrec
1107 J("['a' 23 [] ['b' 88 [] ['c' 44 [] []]]] 'c' Tree-Delete ")
1110 ['a' 23 [] ['b' 88 [] []]]
1115 J("['a' 23 [] ['b' 88 [] ['c' 44 [] []]]] 'b' Tree-Delete ")
1118 ['a' 23 [] ['c' 44 [] []]]
1123 J("['a' 23 [] ['b' 88 [] ['c' 44 [] []]]] 'a' Tree-Delete ")
1126 ['b' 88 [] ['c' 44 [] []]]
1131 J("['a' 23 [] ['b' 88 [] ['c' 44 [] []]]] 'der' Tree-Delete ")
1134 ['a' 23 [] ['b' 88 [] ['c' 44 [] []]]]
1139 J('[] [4 2 3 1 6 7 5 ] [0 swap Tree-add] step')
1142 [4 0 [2 0 [1 0 [] []] [3 0 [] []]] [6 0 [5 0 [] []] [7 0 [] []]]]
1147 J("[4 0 [2 0 [1 0 [] []] [3 0 [] []]] [6 0 [5 0 [] []] [7 0 [] []]]] 3 Tree-Delete ")
1150 [4 0 [2 0 [1 0 [] []] []] [6 0 [5 0 [] []] [7 0 [] []]]]
1155 J("[4 0 [2 0 [1 0 [] []] [3 0 [] []]] [6 0 [5 0 [] []] [7 0 [] []]]] 4 Tree-Delete ")
1158 [3 0 [2 0 [1 0 [] []] []] [6 0 [5 0 [] []] [7 0 [] []]]]
1161 ## Appendix: The source code.
1166 fourth == rest_two rest first
1167 ?fourth == [] [fourth] [] ifte
1168 first_two == uncons uncons pop
1171 rest_two == rest rest
1173 _Tree_T> == [dipd] cinf
1174 _Tree_T< == [dipdd] cinf
1176 _Tree_add_P == over [popop popop first] nullary
1177 _Tree_add_T> == ccons _Tree_T<
1178 _Tree_add_T< == ccons _Tree_T>
1179 _Tree_add_Ee == pop swap roll< rest_two ccons
1180 _Tree_add_R == _Tree_add_P [_Tree_add_T>] [_Tree_add_Ee] [_Tree_add_T<] cmp
1181 _Tree_add_E == [pop] dipd Tree-new
1183 _Tree_iter_order_left == [cons dip] dupdip
1184 _Tree_iter_order_current == [[F] dupdip] dip
1185 _Tree_iter_order_right == [fourth] dip i
1186 _Tree_iter_order_R == _Tree_iter_order_left _Tree_iter_order_current _Tree_iter_order_right
1188 _Tree_get_P == over [pop popop first] nullary
1189 _Tree_get_T> == [fourth] dipd i
1190 _Tree_get_T< == [third] dipd i
1191 _Tree_get_E == popop second
1192 _Tree_get_R == _Tree_get_P [_Tree_get_T>] [_Tree_get_E] [_Tree_get_T<] cmp
1194 _Tree_delete_rightmost == [?fourth] [fourth] while
1195 _Tree_delete_clear_stuff == roll> popop rest
1196 _Tree_delete_del == dip cons dipd swap
1197 _Tree_delete_W == dup _Tree_delete_rightmost first_two over
1198 _Tree_delete_E.0 == _Tree_delete_clear_stuff [_Tree_delete_W] _Tree_delete_del
1199 _Tree_delete_E == [[[pop third not] pop fourth] [[pop fourth not] pop third] [[_Tree_delete_E.0] cinf]] cond
1200 _Tree_delete_R0 == over first swap dup
1201 _Tree_delete_R1 == cons roll> [_Tree_T>] [_Tree_delete_E] [_Tree_T<] cmp
1203 Tree-new == swap [[] []] ccons
1204 Tree-add == [popop not] [_Tree_add_E] [] [_Tree_add_R] genrec
1205 Tree-iter == [not] [pop] roll< [dupdip rest_two] cons [step] genrec
1206 Tree-iter-order == [not] [pop] [dup third] [_Tree_iter_order_R] genrec
1207 Tree-get == [pop not] swap [] [_Tree_get_R] genrec
1208 Tree-delete == [pop not] [pop] [_Tree_delete_R0] [_Tree_delete_R1] genrec