Depth of a tree (Haskell)

前端 未结 3 679
灰色年华
灰色年华 2021-01-23 16:25

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

3条回答
  •  北海茫月
    2021-01-23 16:55

    Use Foldable to get single values out, use Functor to map functions

    user2407038'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 []]
    

    Use a Monoid to combine values

    A 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
    

    The most general fold you can make

    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  
    

    Generalised fold for your tree

    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]
    

提交回复
热议问题