[英]Fold Tree Function
I'm trying to write a fold function for a tree:我正在尝试为一棵树写一个折叠 function :
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
This code nearly works.这段代码几乎可以工作。 However not always.然而并非总是如此。 For example it won't reconstruct the tree exactly the same as the original.例如,它不会重建与原始树完全相同的树。
GHC is good at folding things. GHC善于折叠东西。 The very structure of your type contains enough information for your desired in-order traversal strategy to be obvious to the machine. 您的类型的结构包含足够的信息,以便您所需的有序遍历策略对于机器来说是显而易见的。 To invoke the magic spell, utter the words " deriving Foldable
!" 要调用魔法咒语,请说出“ deriving Foldable
!” and GHC will write your function for you. GHC将为您编写您的功能。
{-# LANGUAGE DeriveFoldable #-}
data BinaryTree a = Leaf
| Node (BinaryTree a) a (BinaryTree a)
deriving Foldable
Now we have 现在我们有
foldTree = foldr
An interesting corollary here is that you can vary the traversal order by varying the shape of the type. 这里有一个有趣的推论是,您可以通过改变类型的形状来改变遍历顺序。
While we're here, a note on your requirements. 我们在这里时,请注意您的要求。 You want to implement a function, using foldr
, which takes a tree apart and puts it back together exactly the same, equivalent to id
. 你想使用foldr
实现一个函数,它将树分开并将它重新组合在一起,完全相同,相当于id
。 This is not possible . 这是不可能的 。 foldr
provides sequential access to the elements of the Foldable
structure, erasing information like the precise position of the element within the tree. foldr
提供对Foldable
结构元素的顺序访问,删除信息,如树中元素的精确位置。 At best, you can build a list-shaped tree, with elements appearing along the right spine: 充其量,您可以构建一个列表形状的树,其中元素出现在右侧脊柱上:
toListShapedTree = foldr (Node Leaf) Leaf
What you want is a catamorphism : 你想要的是一个catamorphism :
cata :: (b -> a -> b -> b) -> b -> BinaryTree a -> b
cata node leaf Leaf = leaf
cata node leaf (Node l x r) = node (cata node leaf l) x (cata node leaf r)
Note the extra parameter to the node
argument! 注意node
参数的额外参数! This specification gives the folding function access to the arguments of the Node
constructor. 此规范为折叠函数提供了对Node
构造函数参数的访问。 Unlike Foldable
, the type of a structure's catamorphism is specific to that structure. 与Foldable
不同,结构的类似于该结构的类型。 We don't lose information by viewing everything as a list. 我们不会通过查看列表中的所有内容来丢失信息。 Now you can write: 现在你可以写:
cataId = cata Node Leaf
If you're dead set on using foldr
for this, one strategy could be to take the positional information with you. 如果你已经开始使用foldr
,那么一种策略就是随身携带位置信息。 First label each element with its position , then in the fold use that data to reconstruct the tree. 首先用位置标记每个元素 ,然后在折叠中使用该数据重建树。 Seems like hard work to me. 对我来说似乎很辛苦。
I think you are looking for this kind of fold: 我想你正在寻找这种折叠:
foldTree :: (a -> b -> b) -> b -> BinaryTree a -> b
foldTree _ base Leaf = base
foldTree fn base (Node left a right) = foldTree fn base' left
where
base' = fn a base''
base'' = foldTree fn base right
This is, roughly, what is being generated by the automatic deriving Foldable
. 这大致是自动deriving Foldable
生成的内容。
The above is a sequential fold, which first folds over the left part, then over the middle element, then over the right. 以上是顺序折叠,首先在左侧部分折叠,然后在中间元素上折叠,然后在右侧折叠。
An equivalent but less efficient variant is to convert the tree to a list with an in-visit and then apply a foldr fn base
over the result. 等效但效率较低的变体是将树转换为具有访问的列表,然后在结果上应用foldr fn base
。 One can "spread" the foldr
over all the list generation, recovering the code above. 可以在所有列表生成中“扩展” foldr
,恢复上面的代码。
I think you need to combine the middle point before going to the right subtree : 我认为你需要在转到正确的子树之前结合中间点:
foldTree :: (a -> b -> b) -> b -> BinaryTree a -> b
foldTree _ base Leaf = base
foldTree fn base (Node left a right) = foldTree fn middlePoint right
where leftFold = foldTree fn base left
middlePoint = fn a leftFold
I came here after struggling with an exercise in Allen & Moronuki's Haskell Programming from first principles . 我从Allen&Moronuki的Haskell编程中挣扎着从第一原理开始练习后来到这里。 This question sounds like the same exercise, so, for what it's worth, from one beginner to another, here's what I came up with. 这个问题听起来像是同样的练习,所以,从一个初学者到另一个,这是值得的,这就是我想出的。
I see three ways to "fold" (or catamorph) a binary tree. 我看到三种“折叠”(或catamorph)二叉树的方法。 When folding a sequence, there are two ways to do it: fold left and fold right. 折叠序列时,有两种方法:向左折叠和向右折叠。 One way is to start by applying the given function to (1) the head of the list and (2) the return value of the recursive call applied to the tail of the list. 一种方法是首先将给定函数应用于(1)列表的头部,以及(2)应用于列表尾部的递归调用的返回值。 That's a fold right. 这是一个正确的折叠。
The other way to do a sequential fold is to start by recursively calling fold on (1) the return value of the given function applied to the head of the list and (2) the tail of the list. 执行顺序折叠的另一种方法是首先递归调用fold on(1)应用于列表头部的给定函数的返回值,以及(2)列表的尾部。 That's a fold left. 这是一个折叠。
But in a binary tree each value can have two "subsequent" values, rather than one as in a list. 但是在二叉树中,每个值可以有两个 “后续”值,而不是列表中的一个值。 So there must be two recursive calls to the fold. 所以必须有两个递归调用折叠。 The call to the passed function thus can either be outside the two recursive calls, inside the two, or else between them. 因此,对传递函数的调用可以在两个递归调用之外,在两个之内,或者在它们之间。 If I call those each left, right and center folds, I get these: 如果我打电话给左,右和中间折叠,我得到这些:
-- Fold Right for BinaryTree
foldTreer :: (a -> b -> b) -> b -> BinaryTree a -> b
foldTreer f z Leaf = z
foldTreer f z (Node left a right) =
f a (foldTreer f (foldTreer f z left) right)
-- Fold Left for Binary Tree
foldTreel :: (a -> b -> b) -> b -> BinaryTree a -> b
foldTreel f z Leaf = z
foldTreel f z (Node left a right) =
foldTreel f (foldTreel f (f a z) left) right
-- Fold Center for Binary Tree
foldTreec :: (a -> b -> b) -> b -> BinaryTree a -> b
foldTreec f z Leaf = z
foldTreec f z (Node left a right) =
foldTreec f (f a (foldTreec f z left)) right
The first time I ever looked at Haskell was a couple weeks ago, so I could be totally wrong about everything, but that's the way it seems to me. 几周前我第一次看到Haskell,所以我对所有事情都完全错了,但这就像我看来的那样。
Thanks for the answers. 谢谢你的回答。 I think I will just leave it as is for these reasons: 我想我会因为以下原因而保留原样:
foldTree (:) [] tree
will create a list of nodes but not in order. foldTree (:) [] tree
将创建节点列表但不按顺序。 However foldTree (+) 0 tree
creates a total where order is irrelevant. 但是, foldTree (+) 0 tree
会创建一个总数,其中订单无关紧要。 I think you are searching foldl one;我认为您正在搜索foldl one;
foldl :: (b -> a -> b) -> b -> Tree a -> b
foldl _ e Leaf = e
foldl f e (Node x l r) = foldl f (f (foldl f e x) l) r
This will work for you这对你有用
Also if you need foldr ;另外,如果您需要foldr ;
foldr :: (a -> b -> b) -> b -> Tree a -> b
foldr _ base Leaf = base
foldr fn base (Node left a right) = fn a (foldr fn acc right)
where acc = foldr fn base left
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.