我正在试图弄清楚如何计算Haskell中一般树的深度。我可以找出简单二叉树的解决方案,但不能用于任何叶子的一般树。
这是我对二叉树的代码。
--depth of a binary tree.
depth :: Tree a -> Int
depth Nil = 0
depth (Node n x1 x2) = 1 + max (depth x1) (depth x2)
如何修改普通树?一般树木包含一系列树木,这就是我遇到困难的地方。
其次,我想把树变成一个列表(所以我可以做一些操作,比如计算总和等)。
同样,我可以想出二进制树,但不能用于普通树。
--tree into a list.
treeToList:: Tree a -> [a]
treeToList Nil = []
treeToList (Node n x1 x2)
= collapse x1 ++ [n] ++ collapse x2
答案 0 :(得分:4)
Foldable
获取单个值,使用Functor
映射函数 user2407038's good answer向您展示如何手动编写Foldable
个实例,这是非常好的建议,您可以foldMap
使用treeToList
,而且还可以使用{-# LANGUAGE DeriveFunctor, DeriveFoldable #-}
import Data.Monoid
import Data.Foldable
data Tree a = Node a [Tree a]
deriving (Show,Functor,Foldable)
使方便的其他功能。
GHC允许您自动派生这些实例:
example :: Tree Int
example = Node 3 [Node 2 [], Node 5 [Node 2 [],Node 1 []],Node 10 []]
-- 3
-- |
-- +--+-----+
-- 2 5 10
-- |
-- +--+
-- 2 1
让我们用一个例子来测试一下:
fmap
让我们使用> 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 []]
将所有内容乘以10:
Monoid
mappend
组合值 Monoid允许您组合(mempty
)值,并且具有名为mempty = []
的无操作/身份值。
列表是一个Monoid,mappend = (++)
和(+)
,数字是不止一种方式的moinoids,例如,使用Sum
((*)
monoid),使用{{1 (Product
monoid),使用最大值(我必须手写Max
幺半群)。
我们将使用foldMap
使用我们想要使用的monoid来标记Ints:
> foldMap Sum example
Sum {getSum = 23}
> foldMap Product example
Product {getProduct = 600}
> foldMap Max example
Max {unMax = 10}
你可以根据自己的喜好定义自己的monoid - 这里是如何制作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
在this great question中有很好的答案,Haskell的首席提问者MathematicalOrchid询问如何将折叠概括为其他数据类型。这个问题的答案很棒,值得一读。
广义fold只是用函数替换数据类型的每个构造函数,并计算得到一个值。
手动方式是查看每个构造函数的类型,并创建一个函数,它接受一个函数参数来匹配每个构造函数和一个参数本身的参数,并返回一个值。
<强>示例:强>
[]
有两个构造函数,(:) :: a -> [a] -> [a]
和[] :: [a]
所以
foldList :: (a -> l -> l) -> l -> [a] -> l
foldList useCons useEmpty = f where
f (a:as) = useCons a (f as)
f [] = useEmpty
Either a b
有两个构造函数,Left :: a -> Either a
和Right :: a -> Either
所以
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)
我们可以用它来做任何我们想做的事情到树上:
-- 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
让我们测试一下:
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]
答案 1 :(得分:2)
考虑将模式概括为列表:
data Tree a = Node a [Tree a] | Nil
depth Nil = 0
depth (Node _ [a]) = 1 + depth a
depth (Node _ [a,b]) = 1 + max (depth a) (depth b)
depth (Node _ [a,b,c]) = 1 + max (max (depth a) (depth b)) (depth c)
etc...
嗯,你所做的就是找到每个子树的深度(map depth
),然后找到这些数字的最大值(maximum
):
depth Nil = 0
depth (Node _ a) = 1 + maximum (map depth a)
您可以以相同的方式展平树,只需map
超过子树:
treeToList (Node n a) = n : concat (map treeToList a)
你必须使用concat,因为map collapse
返回一个列表列表而你只需要一个列表。或者,您可以为Foldable
类型类定义一个实例,然后自动获取toList :: Foldable t => t a -> [a]
import Data.Foldable
import Data.Monoid
instance Foldable Tree where
foldMap f Nil = mempty
foldMap f (Node a n) = f a `mappend` mconcat (map foldMap n)
如果仔细仔细检查foldMap
的定义,您会发现它只是一个更通用的treeToList
,其中:
被mappend
替换为[]
1}} mempty
。那么你可以根据幺半群treeToList
编写([], ++)
这是合乎逻辑的:
data List a = List {getList :: [a]}
instance Monoid (List a) where
mempty = List []
mappend (List a) (List b) = List (a ++ b)
treeToList = getList . foldMap (List . (:[]))
答案 2 :(得分:1)
一些指示:
查看map
函数,该函数允许您将函数应用于列表中的每个元素。在您的情况下,您希望将depth
应用于子项列表中的每个Tree a
。
获得该部分后,您必须在列表中找到最大深度。谷歌搜索“haskell max of list”,你会发现你需要的东西。