Now that we’ve built an efficient functional
queue,
we can finally put it to work in a breadth-first numbering solution.
First, let’s define a few trees up front for testing. The first is the
example given in the introduction of the
paper.
The other two are a little trickier, and the twelve-node tree is not binary.
To calculate the visit order, we can perform a simple breadth-first
traversal of the
tree. Now that we have a queue, the solution is pretty close to the pseudocode: Start with the
root node in the queue, and assign it a number. Then recur with three
new parameters: A queue with this node’s children inserted, a list
with this node’s number consed on, and an incremented node number.
When the queue is empty, we’re done:
1234567891011121314151617181920212223242526
(define visit-order(lambda (tree)(define bfs-iter(lambda (queuevisitedn)(if (null? (remqueue))visited(let ((node(car (remqueue)))(new-q(car (cdr (remqueue)))))(if (equal? "leaf"node)(bfs-iternew-qvisitedn)(bfs-iter(ins-items(cdr node)new-q)(cons (cons (car node)n)visited)(+ 1n)))))))(bfs-iter(instreeempty-q)'()1)))(check-equal?(visit-orderexample-tree)'(("C".4)("D".3)("B".2)("A".1))"Visit-order searches a tree breadth-first and returns a list of nodes and their numbers.")(check-equal?(visit-orderfive-nodes)'(("E".5)("D".4)("C".3)("B".2)("A".1)))(check-equal?(visit-ordertwelve-nodes)'(("L".12)("K".11)("J".10)("I".9)("H".8)("G".7)("F".6)("E".5)("D".4)("C".3)("B".2)("A".1))"Visit-order works on non-binary trees.")
It’s possible to map a visit queue back to a binary tree (check out
Michael’s concise Haskell
solution), but I
wanted a solution that would work for all trees. In the end, I settled for performing a second depth-first
traversal
to label nodes. This walk-map function works like a recursive map,
traversing a nested structure and applying a function to every
element that’s not a list:
On the plus side, this solution generalizes to non-binary trees, and
is built almost entirely out of Scheme primitives. It’s not as concise
or efficient as I’d like, but I’m happy with my lazy lists and
functional queue, even if the implementation is a little long. You can
find an edited version of my solution
here, and all the code
from these posts as a Gist here.