Trees and continuation passing style

For no reason in particular I decided to revisit tree traversal as a kind of programming kata. There are two main kinds of tree traversal:

Depth first – This is where you go all the way down a tree’s branches first before bubbling up to do work. With a tree like below, you’d hit c before doing any work since it’s the deepest part of the tree (assuming you iterated left first then right)

a
/ \
b e
/ \
c d

Breadth first – This is where you hit all the nodes at the level you’re on before going further. So with the same tree, you’d hit a, then b, then e, then c and d.

Being as I actually hate tree traversal, and having to think about it, I decided that whatever I write better be extensible and clean.

In this case I’m just flattening the list and using a function to return all the edges. This way I can re-use the same depth algorithm for any kind of graph, not just a tree (assuming acyclic). To handle cycles you would need to pass the total processed nodes as an accumulator and test if the current node was already processed and if so skip it.

Breadth first

For the BFS, it’s very similar, except instead of using recursion it uses the standard iterative way of doing it with a queue:

DFS stack agnostic

We can even change the DFS to not use recursion in this case so that it’s agnostic of how deep the tree is. In this scenario, unlike the BFS, you’d use a stack instead of a queue. This way you are pushing on the deepest nodes and then immediately processing them. This contrasts with the queue where you enqueue the deepest nodes but process the queue FIFO (first in first out), meaning you process all the nodes at the current depth first before moving to the next depth.

The reverse is only there to be consistent with the left tree descent. Otherwise it goes down the right branch first. This spits out

Depth iterative
1
2
4
5
3
6

DFS with continuation passing

There is yet another way to do tree traversal that is common in functional languages. You can do what is called “continuation passing style”. Doing it this way you can actually get tail recursive code while iterating over multiple tree branches.

Below is some F# code to count the number of nodes in a tree. The tree I’m using as the sample looks like this

But what the hell is going on here? It’s really not apparent when you first look at it what executes what and when.

The trick here is to pass around a function to each iteration that closes over what the next work should be. To be fair, its hard to wrap your mind around what is happening, so lets trace this out. I’ve highlighted each of the continuations and given them an alias so you can see how they are re-used elsewhere. Each time the continuation is called I also show the expanded form following the ->.

You can see how each iteration captures the work to do next. Eventually the very last work that needs to be done is the first function you passed in as the function seed. In this case, it’s the built in id function that returns whatever value is given to it (which turns out to be 6, which is how many nodes are in the tree). You can see the ordering of the traversal is the exact same as the other DFS traversals earlier, except this time everything is tail recursive.