A simple binary tree node is defined by the record structure
where value, left and right indicate the contents and left and right children, respectively. If the empty tree is represented by '(), a binary tree can be constructed using expressions like the following:(define-record btree (value left right))
representing the tree depicted below:(define tree-1 (make-btree 'a (make-btree 'b (make-btree 'c '() '()) (make-btree 'd '() '())) (make-btree 'e (make-btree 'f '() '()) (make-btree 'g '() '()))))
Let's consider writing a program called inorder to "visit" each tree node using the . A visit will be represented abstractly by the function (visit z), where z is the contents of the node being visited. A typical visit activity might be to print the value.
In deep recursive style, inorder would look like this.(define visit (lambda (z) (display z) (newline)))
(define inorder (lambda (tree) (cond [(null? tree) '()] ; no visit to a null tree node [else (inorder (btree->left tree)) (visit (btree->value tree)) (inorder (btree->right tree))])))
Now for something completely different...
The next exercise shows how limited a purely functional visit procedure can be in the purely functional traversal.
(define tree-2 (make-btree 20 (make-btree 10 (make-btree 5 () ()) (make-btree 7 () ())) (make-btree 12 (make-btree 6 () ()) (make-btree 8 () ()))))
The last exercise illustrates how the visit to each node is isolated from the others. With a clever use of CPS we can fix this. We begin by making visit an input parameter to inorder.
Now consider the CPS version (inorder-k) that you wrote in the previous exercise. You should not find it too difficult to extend this so that visit becomes a parameter to inorder-k.
Note that in inorder-k, continuations will not have been passed any "useful" values. This is because anything useful beyond control structure takes place in visit-k, which does not return a value anyone looks at. Why not change things so that the visit-k function becomes the parameter passed to all continuations? That way, each visit function could manufacture its own successor containing any local state it might need.
Call the new procedure inorder-kv. It will begin something like this.
(define inorder-kv (lambda (tree visit k) (cond [(null? tree) (k visit)] [else (inorder-kv (btree->left tree) visit (lambda (new-visit) ....) )])))
where ID = (lambda (x) x) and(inorder-kv tree-2 (xvisit 0) (lambda (new-visit) (new-visit 'done ID)))
(define xvisit (lambda (n) (lambda (v k) (if (eq v 'done) (k n) (begin (printf "~a: ~a~n" n v) (k (xvisit (1+ n))))))))
produces the following output:(inorder-kv tree-2 (yvisit 0) (lambda (new-visit) (new-visit 'done ID)))
0 + 5 = 5 5 + 10 = 15 15 + 7 = 22 22 + 20 = 42 42 + 6 = 48 48 + 12 = 60 60 + 8 = 68 68