I am interested in methods for serializing and deserializing a binary tree in breadth-first order. The first part, serialization, is equivalent to performing a levelorder traversal where we preserve positioning information via the appropriate placing of null characters. Define a binary tree type:
data Tree a = Nil | Node a (Tree a) (Tree a)
Then an example of such a serialization is:
[2,1,3,#,0,#,#]
which serializes a binary tree of the form:
Node 2 (Node 1 Nil (Node 0 Nil Nil)) (Node 3 Nil Nil)
There is a gist on Github that details a clever way to do this in Haskell making use of laziness and infinite streams.
For serialization, there is a recursive version and a corecursive version. The corecursive version is as follows, adapted from that gist discussion:
bflist' :: Tree a -> [Maybe a]
bflist' t = map toMaybe q
where
q = t : go 1 q
go :: Int -> [Tree a] -> [Tree a]
go 0 _ = []
go _ [] = []
go i (Nil : q') = Nil : go (i-1) q'
go i (Node _ l r : q') = l : r : go (i+1) q'
toMaybe Nil = Nothing
toMaybe (Node x _ _) = Just x
There might be something a bit wonky about it because it creates more Nothings than I think it should. But I believe the general approach is sound.
The recursive version is the following:
-- | A 'fill-in-at-the-end' version of `zipWith`.
zipWith' :: (a -> a -> a) -> [a] -> [a] -> [a]
zipWith' _ xs [] = xs
zipWith' _ [] xs = xs
zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys
levelorder' :: Tree a -> [[Maybe a]]
levelorder' Nil = [[Nothing]]
levelorder' (Node a l r) = [Just a] : zipWith' (++) (levelorder' l) (levelorder' r)
serialize :: Tree a -> [Maybe a]
serialize = concat . levelorder'
To me, this seems very nice because writing a levelorder traversal of the form Tree a -> [[a]] is very basic, and this is a trivial modification of that. But that's just my opinion.
For deserialization, however, I don't know how to do it other than the way given in the gist, which is corecursive:
data SS a = a :< SS a
infixr 5 :<
-- | Build a complete binary tree from a list of its breadth-first
-- traversal.
bft :: [a] -> Tree a
bft xs = tree
where
-- `subtrees` is a stream of all proper subtrees of the result tree,
-- in breadth-first order, followed by infinitely many empty trees.
-- We form each tree in the result by combining a label from the input
-- list with consecutive subtrees.
tree :< subtrees = go xs subtrees
go :: [a] -> SS (Tree a) -> SS (Tree a)
go (a : as) ys = Node a b1 b2 :< go as bs
where
{-# NOINLINE b2bs #-}
b1 :< b2bs = ys
b2 :< bs = b2bs
go [] _ = fix (Nil :<)
I would like to know whether there is a recursive analogue of this, or if this is the best we can do.
I believe there is some sort of pointer-based imperative analogue, but I have long since forgotten it. In this question, I am looking for a non-pointer based version if it's possible, because Haskell's lists don't play well with indexing and I'd like to know if it's feasible to stay away from Seq or Vector.