Drawing Trees
Posted on December 30, 2018
Tags: Haskell
For a bunch of algorithms it’s handy to get a quick-and-dirty visualization of a tree. Data.Tree has a tree-drawing function, but its output is too noisy for my taste, and so doesn’t really illustrate the underlying structure in a way I find helpful. This version uses the unicode box-drawing characters to give an output that’s midway between what is provided in Data.Tree and a full-blown SVG diagram. This makes it perfect for debugging tree-based algorithms while you’re writing them.
For the example tree in Wikipedia’s article on breadth-first search, it gives the following output:
┌─9
┌5┤
┌2┤ └10
│ └6
1┼3
│ ┌11
│ ┌7┤
└4┤ └12
└8
module TreeDrawing where
import Data.Tree (Tree(..))
import Data.List (intercalate)
drawTree :: Tree String -> String
= (unlines . filter content . flatten) (foldr go undefined maxLengths withLength)
drawTree tr where
= fmap (\x -> (length x, x)) tr
withLength = lwe withLength (repeat 0)
maxLengths Node x xs) (q:qs) = max (fst x) q : foldr lwe qs xs
lwe (
= any (`notElem` " │")
content = ls ++ [x] ++ rs
flatten (ls,x,rs) = (map lf ls, f x, map rf rs)
mapZipper lf f rf (ls,x,rs) = case splitAt (length xs `div` 2) xs of (ls,x:rs) -> (ls,x,rs)
toZipper xs
Node (l,x) []) = ([],replicate (m-l) '─' ++ x,[])
go m ls (Node (l,x) [y]) = mapZipper pad link pad (ls y)
go m ls (where
= m + 1
padding = (++) (replicate padding ' ')
pad = replicate (m-l) '─' ++ x ++ "─" ++ z
link z Node (l,x') xs) = mapZipper pad link pad (toZipper (intercalate ["│"] ([ysh] ++ ysm ++ [ysl])))
go m ls (where
= replicate (m-l) '─' ++ x'
x = map ls xs
ys
= flatten (mapZipper (' ':) ('┌' :) ('│':) (head ys))
ysh = flatten (mapZipper ('│':) ('└' :) (' ':) (last ys))
ysl = map (flatten . mapZipper ('│':) ('├':) ('│':)) (init (tail ys))
ysm
= (++) (replicate m ' ')
pad
'│':zs) = x ++ "┤" ++ zs
link ('├':zs) = x ++ "┼" ++ zs
link ('┌':zs) = x ++ "┬" ++ zs
link ('└':zs) = x ++ "┴" ++ zs link (