repmin problem is pretty well-known. We are given a data type for trees:
data Tree a = Leaf a | Fork (Tree a) a (Tree a) deriving Show
We need to write a function down (repmin) which would take a tree of numbers and replace all numbers in it by their minimum in a single pass. It is also possible to print the tree out along the way (let us say function repminPrint does this). Both repmin and pre-, post- and in-order repminPrint could be written down easily using value recursion. Here is an example for in-order repminPrint:
import Control.Arrow
replaceWithM :: (Tree Int, Int) -> IO (Tree Int, Int)
replaceWithM (Leaf a, m) = print a >> return (Leaf m, a)
replaceWithM (Fork l mb r, m) = do
(l', ml) <- replaceWithM (l, m)
print mb
(r', mr) <- replaceWithM (r, m)
return (Fork l' m r', ml `min` mr `min` mb)
repminPrint = loop (Kleisli replaceWithM)
But what if we want to write level-order repminPrint down?
My guess is that we cannot use the queue as we need the ml and mr to update the binding for m. I cannot see how this could be down with a queue. I wrote down an instance for level-order Foldable Tree to show what I mean:
instance Foldable Tree where
foldr f ini t = helper f ini [t] where
helper f ini [] = ini
helper f ini ((Leaf v) : q = v `f` helper f ini q
helper f ini ((Fork l v r) : q) = v `f` (helper f ini (q ++ [l, r]))
As you can see, we do not run anything on l and r during the current recursive call.
So, how could this be done? I would appreciate hints instead of full solutions.
I think the best way to accomplish what you're looking to do here is with a traversal (in the sense of the
Traversableclass). First, I'm going to generalise a little bit to rose trees:All of the functions I show should be pretty straightforward to change into the tree definition you have given, but this type is a little more general and shows some of the patterns a little better I think.
Our first task, then, is to write the
repminfunction on this tree. We also want to write it using the derivedTraversableinstance. Luckily, the pattern done byrepmincan be expressed using a combination of the reader and writer applicatives:While we're using the monad transformer version of
WriterThere of course we don't need to, since Applicatives always compose.The next step is to turn this into the
repminPrintfunction: for this, we will need theRecursiveDoextension, which allows us to tie the knot in theunloopfunction even while we're inside the IO monad.Right: so at this stage, we have managed to write a version of
repminPrintwhich uses any generic traversal to do therepminfunction. Of course, it still is in-order, rather than breadth-first:What's missing now is a traversal which walks over the tree in breadth-first, rather than depth-first, order. I'm going to use the function I wrote here:
All in all, that makes the following a single-pass, breadth-first
repminPrintusing an applicative traversal: