如何编写a->类型的函数? b - > b - > b用于折叠树

时间:2016-10-10 22:14:04

标签: haskell tree append fold

一些背景:我在Haskell中有以下类型的foldT函数(如foldr但对于树)。

foldT :: (a -> b -> b -> b) -> b -> Tree a -> b 

此foldT仅将类型(a - > b - > b - > b)作为输入函数。

我正在尝试找到一种方法将我的树转换为列表,并且无法想出一种方法来使我的追加函数采用形式(a - > b - > b - > b)。

以下内容无效,因为它的类型不正确:

append x y z = append x:y:z 

任何帮助将不胜感激。

谢谢!

4 个答案:

答案 0 :(得分:6)

由于你还没有张贴,我会假设你的树是......

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

... a -> b -> b -> b的{​​{1}}参数将foldT构造函数的字段与它们声明的顺序相同。

  

我正在尝试找到一种方法将我的树转换为列表

让我们按照以下类型开始解决这个问题:

Node

您希望将其展平为一个列表,因此您的函数的结果类型必须为foldT :: (a -> b -> b -> b) -> b -> Tree a -> b

[a]

这让我们知道如何继续:

treeToList :: Tree a -> [a]

使用:

treeToList t = foldT f z t

现在,继续论证。 f :: a -> [a] -> [a] -> [a] z :: [a] t :: Tree a 将代替无价值的z插入,因此它必须是Leaf。至于[],它必须将从左右分支创建的子列表与f中的值直接组合在一起。假设有序遍历,我们有:

Node

或者,没有提及 treeToList t = foldT (\x l r -> l ++ x : r) [] t

t

就是这样。需要注意的是,重复使用左嵌套 treeToList = foldT (\x l r -> l ++ x : r) [] (在这种情况下会发生,因为(++)递归地向下走分支)可能会变得非常低效。在您关心性能的情况下,值得考虑实现连接函数的替代方法,例如difference lists

P.S。:关于术语的说明。说一个函数“像折叠器但是对于树”是不明确的,因为有两种众所周知的函数类似于foldT。首先,您拥有foldr类的方法(参见Benjamin Hodgson's answer),无论您做什么,它都会将树折叠成一个列表。然后是更强大的 catamorphisms ,例如你正在使用的Foldable,它们能够利用树结构。

答案 1 :(得分:3)

哎呀,你为什么要写代码? GHC可以自动填写the Foldable class的实例,其中包含您正在寻找的toList

{-# LANGUAGE DeriveFoldable #-}
data Tree a = Leaf | Node a (Tree a) (Tree a) deriving Foldable

treeToList :: Tree a -> [a]
treeToList = toList

由于treeToList构造函数的字段顺序,Node的定义将执行pre-order traversal

答案 2 :(得分:0)

如果要将树转换为列表,那么函数相对容易, 所以基本上如果你有树结构,如:

data Tree a = Leaf | Node (Tree a) a (Tree a) deriving (Eq, Ord, Show)

然后你必须从树中提取值并将第一个值添加到空列表中,然后在list(++)运算符的帮助下以递归方式将相同的方法应用于树的其他分支。将所有结果附加到单个列表中,即:

toList :: Tree a -> [a]
toList Leaf                    =    []
toList (Node left value right) =    [value] ++ toList left ++ toList right

然后折叠它,首先:

foldT :: (a -> b -> b -> b) -> b -> Tree a -> b

函数签名缺少一个参数,让我们看看签名: a(第一个参数) - > b(第二) - > b(第三) - > b(返回类型)该函数采用 3个参数 /参数,但您提供的签名 只有1 b类型(即我猜测是你的累加器),所以基本上这将是你正在寻找的函数签名:

foldT :: (a -> b -> b) -> b -> Tree a -> b

现在我们可以使用这个签名,然后让我们进入函数:

foldT :: (a -> b -> b) -> b -> Tree a -> b
foldT f acc Leaf                    =   acc
foldT f acc (Node left value right) =   foldT f (foldT f (f value acc) left) right

虽然我不认为我应该给你答案,你应该用递归数据结构练习更多,这样你就可以更好地理解如何遍历它们。

PS:如果你打算投票给我,那么至少要解释一下原因!

答案 3 :(得分:-1)

每次开始的第一件事就是开始匹配模式:)那时,它非常机械化!

假设你有:

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

让我们开始匹配模式吧! :)

foldT :: (a -> b -> b -> b) -> b -> Tree a -> b
foldT f z ...

第一个Tree构造函数是什么?这是Leaf

foldT :: (a -> b -> b -> b) -> b -> Tree a -> b
foldT f z Leaf = ...

我们可以放在那里?我们需要b类型的东西......我们只有一种方法可以获得它。使用z:)

foldT :: (a -> b -> b -> b) -> b -> Tree a -> b
foldT f z Leaf = z

所以,从机械上讲,我们可以继续下一个可能的模式:

foldT :: (a -> b -> b -> b) -> b -> Tree a -> b
foldT f z Leaf           = z
foldT f z (Node t1 x t2) =

好吧,我们可以做到z,但这有点无聊。我们可能希望使用t1xt2。我们可以使用f,其中需要a类型的内容......我们有x :: a:)

foldT :: (a -> b -> b -> b) -> b -> Tree a -> b
foldT f z Leaf           = z
foldT f z (Node t1 x t2) = f x ??? ???

f还有两个类型为b的参数,那么您认为我们可以放在哪里?我们可以z两次,但这有点无聊。我们如何从bt1获取t2