我正在尝试为一棵树写一个fold函数:
data BinaryTree a = Leaf
| Node (BinaryTree a) a (BinaryTree a)
deriving (Eq, Ord, Show)
foldTree :: (a -> b -> b) -> b -> BinaryTree a -> b
foldTree _ base Leaf = base
foldTree fn base (Node left a right) = fn a (foldTree fn acc right)
where acc = foldTree fn base left
这段代码几乎可以正常工作,但并不总是如此。例如,它不能重建出与原始树完全相同的树。
6条答案
按热度按时间fhg3lkii1#
GHC擅长折叠东西。你的类型结构包含了足够的信息,你想要的按序遍历策略对机器来说是显而易见的。要调用这个魔法咒语,说出 ”
deriving Foldable
!",GHC就会为你写函数。现在我们有
这里一个有趣的推论是,您可以通过改变类型的形状来改变遍历顺序。
既然我们在这里,请注意您的需求。您希望使用
foldr
实现一个函数,它可以完全相同地拆分树并将其重新组合在一起,等效于id
。这是不可能的。foldr
提供对Foldable
结构的元素的 * 顺序 * 访问,删除树中元素的精确位置之类的信息。最好的情况是,您可以构建一个列表形状的树,元素沿着右 Backbone.js 出现:你想要的是一个 * 退化 *:
请注意
node
参数的额外参数!此规范为折叠函数提供了对Node
构造函数参数的访问。与Foldable
不同,结构的退化类型是特定于该结构的。我们不会因为将所有内容作为列表查看而丢失信息。现在,您可以编写:如果你执意使用
foldr
,一个策略是把位置信息带在身边。首先用位置标记每个元素,然后在折叠中使用这些数据来重建树。对我来说这是一个很难的工作。guicsvcw2#
我想你是在寻找这种褶皱:
这大概就是自动
deriving Foldable
所生成的内容。上面是一个顺序折叠,首先折叠左边的部分,然后折叠中间的部分,然后折叠右边的部分。
一个等效但效率较低的变体是将树转换为带有in-visit的列表,然后在结果上应用
foldr fn base
。可以将foldr
“分散”到所有列表生成中,恢复上面的代码。nnvyjq4y3#
我在艾伦& Moronuki的 Haskell Programming from first principles 中做了一个练习后来到这里。这个问题听起来像是同一个练习,所以,不管它有什么价值,从一个初学者到另一个初学者,这里是我想到的。
我看到了三种折叠二叉树的方法。当折叠一个序列时,有两种方法:左折和右折。一种方法是将给定的函数应用于(1)列表的头部和(2)应用于列表尾部的递归调用的返回值。这就是右折。
另一种顺序折叠的方法是,首先递归地调用fold:(1)给定函数的返回值应用于列表的头部,(2)应用于列表的尾部,这就是左折叠。
但是在二叉树中,每个值可以有 * 两个 *“后续”值,而不是列表中的一个。因此,必须有两个递归调用fold。因此,对传递函数的调用可以在两个递归调用之外,也可以在两个递归调用之内,或者在它们之间。如果我分别调用左、右和中心fold,我会得到以下结果:
我第一次看到 haskell 是在几周前,所以我可能什么都错了,但在我看来就是这样。
9bfwbjaz4#
我认为你需要合并中间点,然后再去右边的子树:
2w2cym1i5#
谢谢你的回答。我想我会让它保持原样,因为这些原因:
1.在问题中给出了类型签名,所以我认为这就是他们想要的。
1.问题提到“任何遍历顺序都可以”。
1.它可以工作,只是不按顺序。例如,
foldTree (:) [] tree
将创建一个节点列表,但不按顺序。然而,foldTree (+) 0 tree
创建一个总数,其中顺序是无关紧要的。vcudknz36#
我想你是在找一个
这将为您工作
另外,如果您需要foldr;