I\'m trying to figure out how to calculate the depth of a general tree in Haskell. I can figure out the solution for simple binary trees, but not for general trees with any numb
Foldable
to get single values out, use Functor
to map functionsuser2407038's good answer shows you how to write a Foldable
instance by hand, which is very good advice, and you can use foldMap
not just to make treeToList
, but also to make handy other functions.
GHC lets you derive these instances automatically:
{-# LANGUAGE DeriveFunctor, DeriveFoldable #-}
import Data.Monoid
import Data.Foldable
data Tree a = Node a [Tree a]
deriving (Show,Functor,Foldable)
Let's use an example to test this out:
example :: Tree Int
example = Node 3 [Node 2 [], Node 5 [Node 2 [],Node 1 []],Node 10 []]
-- 3
-- |
-- +--+-----+
-- 2 5 10
-- |
-- +--+
-- 2 1
Let's use fmap
to multiply everything by 10:
> example
Node 3 [Node 2 [], Node 5 [Node 2 [], Node 1 []], Node 10 []]
> fmap (*10) example
Node 30 [Node 20 [],Node 50 [Node 60 [],Node 10 []],Node 100 []]
Monoid
to combine valuesA Monoid lets you combine (mappend
) values, and has a do-nothing/identity value called mempty
.
Lists are a Monoid, with mempty = []
and mappend = (++)
, numbers are moinoids in more than one way, for example, using (+)
(the Sum
monoid), using (*)
(the Product
monoid), using maximum (I had to hand-write the Max
monoid).
We'll use foldMap
to tag the Ints with what monoid we want to use:
> foldMap Sum example
Sum {getSum = 23}
> foldMap Product example
Product {getProduct = 600}
> foldMap Max example
Max {unMax = 10}
You can define your own monoid however you like - here's how to make the Max monoid:
instance (Ord a,Bounded a) => Monoid (Max a) where
mempty = Max minBound
mappend (Max a) (Max b) = Max $ if a >= b then a else b
In this great question with great answers, Haskell's top asker, MathematicalOrchid asks how to generalise fold to other data types. The answers to the question are great and worth reading.
A generalised fold simply replaces each constructor of the data type with a function and evaluates to get a value.
The hand-rolled way is to look at the types of each of the constructors, and make a function that takes a function argument to match each constructor and an argument for the object itself, and returns a value.
Examples:
[]
has two constructors, (:) :: a -> [a] -> [a]
and [] :: [a]
so
foldList :: (a -> l -> l) -> l -> [a] -> l
foldList useCons useEmpty = f where
f (a:as) = useCons a (f as)
f [] = useEmpty
Either a b
has two constructors, Left :: a -> Either a
and Right :: a -> Either
so
foldEither :: (a -> e) -> (b -> e) -> Either a b -> e
foldEither useLeft useRight = f where
f (Left a) = useLeft a
f (Right b) = useRight b
generalFold :: (a -> [t] -> t) -> Tree a -> t
generalFold useNode = f where
f (Node a ts) = useNode a (map f ts)
we can use that to do pretty much anything we want to to a tree:
-- maximum of a list, or zero for an empty list:
maxOr0 [] = 0
maxOr0 xs = maximum xs
height :: Tree a -> Int
height = generalFold maxPlus1 where
maxPlus1 a as = 1 + maxOr0 as
sumTree = generalFold sumNode where
sumNode a as = a + sum as
productTree = generalFold productNode where
productNode a as = a * product as
longestPath = generalFold longest where
longest a as = a + maxOr0 as
Let's test them:
ghci> example
Node 3 [Node 2 [],Node 5 [Node 2 [],Node 1 []],Node 10 []]
ghci> height example
3
ghci> sumTree example -- 3 + sum[2, 5+sum[2,1], 10] = 3+2+5+2+1+10
23
ghci> productTree example -- 3*(2*(5*(2*1))*10) = 3*2*5*2*1*10
600
ghci> longestPath example -- 3 + maximum [2, 5+maximum[2,1], 10]
13
ghci> toList example -- 3 : concat [[2], 5:concat[[2],[1]], [10]]
[3,2,5,2,1,10]