我知道文件夹在Leaf上如何工作,但我不知道文件夹在Node上如何工作。如果我们已经有f和z作为参数,则参数\ x z'是什么。假设我们有
tree = Node [Leaf 1, Leaf 2, Node [Leaf 1, Leaf 3]]
此代码的工作方式
foldr (+) 0 tree
data RoseTree a = Leaf a | Node [RoseTree a]
instance Foldable RoseTree where
foldr f z (Leaf x) = f x z
foldr f z (Node ns) =
foldr (\x z' -> foldr f z' x) z ns
答案 0 :(得分:6)
foldr
的{{1}}定义调用Node
列表中的foldr
。然后,在该RoseTree
内部,使用当前累加器作为初始参数,在每个子树上调用foldr
。
基本上,即使函数两次调用foldr
,它每次都针对不同的类型调用它,因此只有一个是递归的。另一个是为foldr
定义的foldr
。
答案 1 :(得分:4)
我们可以使用您给定的示例数据讨论实现方式:
foldr (+) 0 (Node [Leaf 1, Leaf 2, Node [Leaf 1, Leaf 3]])
因此我们这里有一个Node
,因此我们采用第二个子句,因此将其替换为:
foldr (\x z' -> foldr (+) z' x) 0 [Leaf 1, Leaf 2, Node [Leaf 1, Leaf 3]]
外部foldr
因此在列表上起作用,这意味着documentation:
foldr f z [x1, x2, ..., xn] == x1 `f` (x2 `f` ... (xn `f` z)...)
因此,意味着上述foldr
被替换为:
foldr (+) (foldr (+) (foldr (+) 0 (Node [Leaf 1, Leaf 3])) (Leaf 2)) (Leaf 1)
因此外部函数现在为foldr (+) (...) (Leaf 1)
,这是我们foldr
定义的第一子句,因此等于:
(+) 1 (foldr (+) (foldr (+) 0 (Node [Leaf 1, Leaf 3])) (Leaf 2))
然后我们可以撤消foldr (+) (...) (Leaf 2)
表达式,该表达式的处理方式相同:
(+) 1 ((+) 2 (foldr (+) 0 (Node [Leaf 1, Leaf 3])))
或更简单:
1 + 2 + foldr (+) 0 (Node [Leaf 1, Leaf 3])
最后,我们再次有一个foldr (+) 0
可以在Node
上运行,因此再次产生了如上所述的评估结果:
1 + 2 + foldr (+) (foldr (+) 0 (Leaf 3)) (Leaf 1)
因此,我们可以再次将外部foldr (+)
评估为:
1 + 2 + (+) 1 (foldr (+) 0 (Leaf 3))
和内部foldr (+)
用于:
1 + 2 + (+) 1 ((+) 3 0)
或更简单:
1 + 2 + 1 + 3 + 0
等效于:
7
是Leaf
中节点的总和。
请务必注意,外部 foldr
(此处用斜体表示)与内部foldr
的功能不同实现中的 foldr
(此处用粗体表示):外部的一个用作函子,而内部的一个是我们在列表中定义的函子。 instance Foldable RoseTree
:
instance Foldable RoseTree where
foldr f z (Leaf x) = f x z
foldr f z (Node ns) = foldr (\x z' -> foldr f z' x) z ns
如果我们因此在foldr
上使用函数Tree
和初始值f
执行z
,则将所有叶子替换为f x z
,(对于foldr (+) 0
就是(+) x 0
或x + 0
而言)。
Node
将导致 folding 值一起折叠,其中将拖尾元素的折叠结果用作与head元素的折叠的初始值。