Haskell-树中的和值

Haskell-树中的和值,haskell,functional-programming,Haskell,Functional Programming,我的树是这样的,每个节点上都可以或不能有一个整数: data Tree = Empty | Node (Maybe Integer) Tree Tree deriving Show 我想对树中的所有值求和,不包括Nothing值,如果树不是空的,但只有Nothing值,只返回Nothing,或者空树为0。这些案子我知道怎么办 我希望思考深度优先遍历是最好的,或者只是一些基本的遍历,但在如何优雅地实现它方面仍有困难 treeValues::Tree->Maybe Integer您可以将树作为一个

我的树是这样的,每个节点上都可以或不能有一个整数:

data Tree = Empty | Node (Maybe Integer) Tree Tree deriving Show
我想对树中的所有值求和,不包括Nothing值,如果树不是空的,但只有Nothing值,只返回Nothing,或者空树为0。这些案子我知道怎么办

我希望思考深度优先遍历是最好的,或者只是一些基本的遍历,但在如何优雅地实现它方面仍有困难

treeValues::Tree->Maybe Integer

您可以将树作为一个实例,并免费获得许多函数,包括
sum

总和::(可折叠的t,数值a)=>t a->a源

sum函数计算结构的数量之和

但您需要将
设置为参数化类型:

data Tree a = Empty | Node (Maybe a) Tree Tree
此外,在GHC 7.10中,几乎所有的Prelude函数都将使用可折叠的和可遍历的类型类而不是列表,然后如果您实现了这些类型类,您可以在树中自由使用它们。

您可以将树作为一个实例,并且您可以免费获得许多函数,包括
sum

总和::(可折叠的t,数值a)=>t a->a源

sum函数计算结构的数量之和

但您需要将
设置为参数化类型:

data Tree a = Empty | Node (Maybe a) Tree Tree
此外,在GHC 7.10中,几乎所有的Prelude函数都将使用
可折叠的
可遍历的
类型类而不是列表,如果您实现了这些类型类,那么您可以在树中自由使用它们。

这里有一个提示:

data Tree a = Empty | Node a (Tree a) (Tree a)

reduce :: (a -> r -> r -> r) -> r -> Tree a -> r
reduce f z = go
    where
        go Empty        = z
        go (Node x l r) = f x (go l) (go r)
这里有一个提示:

data Tree a = Empty | Node a (Tree a) (Tree a)

reduce :: (a -> r -> r -> r) -> r -> Tree a -> r
reduce f z = go
    where
        go Empty        = z
        go (Node x l r) = f x (go l) (go r)

您已经知道如何对列表求和,因此可以先将树转换为列表:

> toList :: Tree -> [Integer]
> toList Empty        = []
> toList (Node a l r) = maybeToList a ++ toList l ++ toList r
>   where maybeToList (Just x) = [x]
>         maybeToList Nothing  = []
现在,您需要区分空树(
empty
)和只包含
内容的树。由于
toList
过滤所有
Nothing
值,这归结为

> sumTree :: Tree -> Maybe Integer
> sumTree Empty = Just 0
> sumTree tree  = case toList tree of
>                  [] -> Nothing      -- all values in the tree are Nothing
>                  xs -> Just $ sum xs       -- some were Just x
但是等等,还有更多!
sumTree
还没那么好。如果我们想计算一个
树的乘积
?嗯,我们可以拿一棵树,把它转换成一个列表,然后使用…一个折叠函数

> type I = Integer -- otherwise the lines get ridiculously long
>
> foldrTree' :: (I -> I -> I) -> I -> Tree -> Maybe I
> foldrTree' _ init Empty = init
> foldrTree' f init tree  = case toList tree of
>                            [] -> Nothing
>                            xs -> Just $ foldr f init xs
> --                                      ^^^^^
现在,只要我们的操作是关联的,我们就可以获取任何
(整数->整数->整数)
并生成单个值:

> productTree :: Tree -> Maybe Integer
> productTree = foldrTree' (*) 1
>
> sumTree' :: Tree -> Maybe Integer
> sumTree' = foldrTree' (+) 0

您已经知道如何对列表求和,因此可以先将树转换为列表:

> toList :: Tree -> [Integer]
> toList Empty        = []
> toList (Node a l r) = maybeToList a ++ toList l ++ toList r
>   where maybeToList (Just x) = [x]
>         maybeToList Nothing  = []
现在,您需要区分空树(
empty
)和只包含
内容的树。由于
toList
过滤所有
Nothing
值,这归结为

> sumTree :: Tree -> Maybe Integer
> sumTree Empty = Just 0
> sumTree tree  = case toList tree of
>                  [] -> Nothing      -- all values in the tree are Nothing
>                  xs -> Just $ sum xs       -- some were Just x
但是等等,还有更多!
sumTree
还没那么好。如果我们想计算一个
树的乘积
?嗯,我们可以拿一棵树,把它转换成一个列表,然后使用…一个折叠函数

> type I = Integer -- otherwise the lines get ridiculously long
>
> foldrTree' :: (I -> I -> I) -> I -> Tree -> Maybe I
> foldrTree' _ init Empty = init
> foldrTree' f init tree  = case toList tree of
>                            [] -> Nothing
>                            xs -> Just $ foldr f init xs
> --                                      ^^^^^
现在,只要我们的操作是关联的,我们就可以获取任何
(整数->整数->整数)
并生成单个值:

> productTree :: Tree -> Maybe Integer
> productTree = foldrTree' (*) 1
>
> sumTree' :: Tree -> Maybe Integer
> sumTree' = foldrTree' (+) 0

关于上述解决方案和意见,我和plus编写了以下建议(请在
ghci
中尝试):


虽然它只返回
0
,但在这两种情况下都只返回
Nothing
给定的值,并且树是
空的
,但我希望它以后能给像我这样的学习者一个提示。

关于上述解决方案和注释,我编写了以下建议(请在
ghci
中尝试):


虽然它只返回
0
,在这两种情况下都只返回
Nothing
给定的值,树是
空的
,但我希望它稍后能给像我这样的学习者一个提示。

如果某些节点是空的,会发生什么?澄清啊,忘了提一下,我编辑了上面的问题。它只是忽略了nothing的值,一棵nothing树被以一种方式对待,而一棵空树被以另一种方式对待,这让人感到相当尴尬。我认为这两者都应该是0。如果一些节点什么都不是,会发生什么?澄清啊,忘了提一下,我编辑了上面的问题。它只是忽略了nothing的值,一棵nothing树被以一种方式对待,而一棵空树被以另一种方式对待,这让人感到相当尴尬。我认为这两者都应该是0。就我而言,
toList=reduce(\x l r->maybeToList x++l++r)[]
@Cirdec:yes。我没看到你的答案。谢谢你的评论,否则我会再次编写一个完全成熟的
实例Foldable=>TreeF
教程。我只想在我的主函数中调用toList,然后调用sumTree。如何很好地嵌套函数调用,比如treeValues::Tree->Maybe Integer treeValues=我想以一种优雅的方式调用toList和sumTree,首先调用toList,然后调用sumTree?根据我的,
toList=reduce(\x l r->maybeToList x++l++r)[]
@Cirdec:Yep.没有看到你的答案。谢谢你的评论,否则我会编写一个完全成熟的
实例Foldable=>TreeF
教程-再次。我只想在我的主函数中调用toList,然后调用sumTree。如何很好地嵌套函数调用,如treeValues::Tree->Maybe Integer treeValues=我想以一种优雅的方式同时调用toList和sumTree,首先调用toList,然后调用sumTree?GHC可以使用自动派生出可折叠的
实例(第7.5.3节).GHC可使用(第7.5.3节)自动导出可折叠的
实例。