一些背景:我在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
任何帮助将不胜感激。
谢谢!
答案 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
,但这有点无聊。我们可能希望使用t1
,x
和t2
。我们可以使用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
两次,但这有点无聊。我们如何从b
和t1
获取t2
?