First, you should decide if the height of a tree will be a field of Node
or not. If yes, then there is no need to compute it. Instead, your tree module should hide the constructors and only expose smart constructors:
data Tree a = Leaf
| Node Integer (Tree a) a (Tree a)
deriving (Show, Eq)-- where Integer is the height (bottom = 0)
getTreeHeight :: Tree a -> Integer
getTreeHeight Leaf = 0
getTreeHeight (Node h _ _ _) = h
leaf :: Tree a
leaf = Leaf
node :: Tree a -> a -> Tree a -> Tree a
node l x r = Node (1 + max (getTreeHeight l) (getTreeHeight r)) l x r
In this case, getTreeHeight
just accesses the pre-computed height.
Or you might want to keep Node
without the height, and compute it separately. Implementing Foldable
won't help, as it only exposes the elements of a data structure, while you need to examine the structure, not the elements.
What you can do is to create a generalized folding function over Tree
, so called catamorphism, which allows you to express functions that consume trees:
data Tree a = Leaf
| Node (Tree a) a (Tree a)
deriving (Show, Eq)-- where Integer is the height (bottom = 0)
treeFold :: (r -> a -> r -> r) -> r -> Tree a -> r
treeFold f z = h
where
h Leaf = z
h (Node l x r) = f (h l) x (h r)
{-# INLINE treeFold #-}
-- Get the height of a tree.
getTreeHeight :: Tree a -> Integer
getTreeHeight = treeFold (\l _ r -> 1 + max l r) 0
Function treeFold
allows you to express all kinds of other functions, to give a few examples:
-- Get the size of a tree.
getTreeSize :: Tree a -> Integer
getTreeSize = treeFold (\l _ r -> 1 + l + r) 0
-- Get the number of leaves.
getTreeLeaves :: Tree a -> Integer
getTreeLeaves = treeFold (\l _ r -> l + r) 1