文件夹如何工作?
谁能解释一下 foldr
工作吗?
举这些例子:
Prelude> foldr (-) 54 [10, 11]
53
Prelude> foldr (\x y -> (x+y)/2) 54 [12, 4, 10, 6]
12.0
我对这些处决感到困惑。有什么建议吗?
Can anybody explain how does foldr
work?
Take these examples:
Prelude> foldr (-) 54 [10, 11]
53
Prelude> foldr (\x y -> (x+y)/2) 54 [12, 4, 10, 6]
12.0
I am confused about these executions. Any suggestions?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
理解foldr的最简单方法是重写您要折叠的列表,而不加任何糖分。
现在
foldr f x
所做的是,它将每个:
替换为中缀形式的f
,并将[]
替换为x
并评估结果。例如:
所以
The easiest way to understand foldr is to rewrite the list you're folding over without the sugar.
now what
foldr f x
does is that it replaces each:
withf
in infix form and[]
withx
and evaluates the result.For example:
so
foldr
从列表的右端开始,并使用您提供的函数将每个列表条目与累加器值组合起来。结果是所有列表元素“折叠”后累加器的最终值。它的类型是:从这里你可以看到列表元素(
a
类型)是给定函数的第一个参数,累加器(b
类型)是第二个。对于第一个例子:
所以你得到的答案是 53。
第二个例子:
所以结果是 12。
编辑:我的意思是添加,这是针对有限列表的。
foldr
也可以处理无限列表,但我认为最好先了解有限情况。foldr
begins at the right-hand end of the list and combines each list entry with the accumulator value using the function you give it. The result is the final value of the accumulator after "folding" in all the list elements. Its type is:and from this you can see that the list element (of type
a
) is the first argument to the given function, and the accumulator (of typeb
) is the second.For your first example:
So the answer you got was 53.
The second example:
So the result is 12.
Edit: I meant to add, that's for finite lists.
foldr
can also work on infinite lists but it's best to get your head around the finite case first, I think.它有助于理解
foldr
和foldl
之间的区别。为什么foldr
称为“右折叠”?最初我认为这是因为它从右到左消耗元素。然而,
foldr
和foldl
都从左到右使用列表。foldl
从左到右评估(左关联)foldr
从右到左评估(右关联)我们可以通过一个使用关联性很重要的运算符的示例来清楚地阐明这种区别。我们可以使用人类的例子,例如运算符“eats”:
这个foldl的语义是:一个人吃了一些鲨鱼,然后吃掉鲨鱼的同一个人又吃了一些鱼,等等。吃者是累加者。
对比一下:
这个
foldr
的语义是:一个人吃掉了一条已经吃掉了一条鱼的鲨鱼,而这条鱼已经吃掉了一些藻类。食物是蓄能器。foldl
和foldr
都是从左到右“剥离”吃者,所以这不是我们将foldl称为“左折叠”的原因。相反,评估的顺序很重要。It helps to understand the distinction between
foldr
andfoldl
. Why isfoldr
called "fold right"?Initially I thought it was because it consumed elements from right to left. Yet both
foldr
andfoldl
consume the list from left to right.foldl
evaluates from left to right (left-associative)foldr
evaluates from right to left (right-associative)We can make this distinction clear with an example that uses an operator for which associativity matters. We could use a human example, such as the operator, "eats":
The semantics of this
foldl
is: A human eats some shark, and then the same human who has eaten shark then eats some fish, etc. The eater is the accumulator.Contrast this with:
The semantics of this
foldr
is: A human eats a shark which has already eaten a fish, which has already eaten some algae. The food is the accumulator.Both
foldl
andfoldr
"peel off" eaters from left to right, so that's not the reason we refer to foldl as "left fold". Instead, the order of evaluation matters.想想
foldr
的 定义:例如
foldr (-) 54 [10,11]
必须等于(-) 10 (foldr (-) 54 [11])
,即再次展开,等于(-) 10 ((-)11 54)
。所以内部运算为11 - 54
,即-43;外部操作是10 - (-43)
,即10 + 43
,因此正如您所观察到的53
。对第二种情况执行类似的步骤,您将再次看到结果如何形成!Think about
foldr
's very definition:So for example
foldr (-) 54 [10,11]
must equal(-) 10 (foldr (-) 54 [11])
, i.e. expanding again, equal(-) 10 ((-) 11 54)
. So the inner operation is11 - 54
, that is, -43; and the outer operation is10 - (-43)
, that is,10 + 43
, therefore53
as you observe. Go through similar steps for your second case, and again you'll see how the result forms!foldr
表示从右侧折叠,因此foldr (-) 0 [1, 2, 3]
生成(1 - (2 - (3 - 0)) )
。相比之下,foldl
生成(((0 - 1) - 2) - 3)
。当运算符不可交换
foldl
和foldr 会得到不同的结果。
在您的例子中,第一个示例扩展为
(10 - (11 - 54))
,给出 53。foldr
means fold from the right, sofoldr (-) 0 [1, 2, 3]
produces(1 - (2 - (3 - 0)))
. In comparisonfoldl
produces(((0 - 1) - 2) - 3)
.When the operators are not commutative
foldl
andfoldr
will get different results.In your case, the first example expands to
(10 - (11 - 54))
which gives 53.理解foldr的一个简单方法是:它将每个列表构造函数替换为所提供函数的应用程序。你的第一个例子将转换为:
10 - (11 - 54)
from:
10 : (11 : [])
我从 Haskell Wikibook 得到的一个很好的建议在这里可能有一些用处:
An easy way to understand
foldr
is this: It replaces every list constructor with an application of the function provided. Your first example would translate to:10 - (11 - 54)
from:
10 : (11 : [])
A good piece of advice that I got from the Haskell Wikibook might be of some use here:
我一直认为 http://foldr.com 是一个有趣的插图。请参阅 Lambda Ultimate 帖子。
I've always thought http://foldr.com to be a fun illustration. See the Lambda the Ultimate post.
仔细阅读此处提供的其他答案并对其进行比较应该已经清楚了这一点,但值得注意的是,所接受的答案可能会对初学者有点误导。正如其他评论者所指出的,Haskell 中执行的计算foldr 并不是“从列表的右手端开始”;而是“从列表的右端开始”。否则,foldr 永远无法在无限列表上工作(在正确的条件下,它在 Haskell 中是这样做的)。
Haskell 的源代码
foldr
函数应该明确这一点:每个递归计算将最左边的原子列表项与列表尾部的递归计算结合起来,即:
其中
a[n]
是初始累加器。因为归约是“在 Haskell 中延迟完成的”,所以它实际上是从左侧开始。这就是我们所说的“惰性求值”,也是 Haskell 的一个著名的显着特征。这对于理解 Haskell 的
foldr
的操作很重要;因为,事实上,foldr
构建并减少计算从左侧递归,二元运算符可以电路有机会在适当的情况下允许无限列表减少foldr。对于初学者来说,说
foldr
中的r
(“右”)和l
(“左”)会减少很多混乱。和foldl
指的是右关联性和左关联性,要么就这样,要么尝试解释 Haskell 惰性求值机制的含义。为了完成您的示例,按照
foldr
源代码,我们构建了以下表达式:再次:
Careful readings of -- and comparisons between -- the other answers provided here should already make this clear, but it's worth noting that the accepted answer might be a bit misleading to beginners. As other commenters have noted, the computation foldr performs in Haskell does not "begin at the right hand end of the list"; otherwise,
foldr
could never work on infinite lists (which it does in Haskell, under the right conditions).The source code for Haskell's
foldr
function should make this clear:Each recursive computation combines the left-most atomic list item with a recursive computation over the tail of the list, viz:
where
a[n]
is the initial accumulator.Because reduction is done "lazily in Haskell," it actually begins at the left. This is what we mean by "lazy evaluation," and it's famously a distinguishing feature of Haskell. And it's important in understanding the operation of Haskell's
foldr
; because, in fact,foldr
builds up and reduces computations recursively from the left, binary operators that can short-circuit have an opportunity to, allowing infinite lists to be reduced byfoldr
under appropriate circumstances.It will lead to far less confusion to beginners to say rather that the
r
("right") andl
("left") infoldr
andfoldl
refer to right associativity and left associativity and either leave it at that, or try and explain the implications of Haskell's lazy evaluation mechanism.To work through your examples, following the
foldr
source code, we build up the following expression:And again:
我认为以简单的方式实现map、foldl 和foldr 有助于解释它们的工作原理。工作示例也有助于我们的理解。
I think that implementing map, foldl and foldr in a simple fashion helps explain how they work. Worked examples also aid in our understanding.
好的,让我们看看参数:
;返回值:
它首先将函数应用于列表中的最后一个元素和空列表结果。然后,它使用此结果和前一个元素重新应用该函数,依此类推,直到它使用某个当前结果和列表的第一个元素来返回最终结果。
Fold 使用接受元素和一些先前折叠结果的函数围绕初始结果“折叠”列表。它对每个元素重复此操作。因此,foldr 从列表的末尾或列表的右侧开始执行此操作。
folr femptyresult [1,2,3,4]
变成f(1, f(2, f(3, f(4, 空结果) ) ) )
。现在只需在评估中遵循括号即可。需要注意的一件重要事情是,提供的函数 f 必须将其自己的返回值作为第二个参数处理,这意味着两者必须具有相同的类型。
资料来源:我的帖子,我从如果您认为它可能有帮助,请使用命令式非柯里化 javascript 观点。
Ok, lets look at the arguments:
return value:
It first applies the function to the last element in the list and the empty list result. It then reapplies the function with this result and the previous element, and so forth until it takes some current result and the first element of the list to return the final result.
Fold "folds" a list around an initial result using a function that takes an element and some previous folding result. It repeats this for each element. So, foldr does this starting at the end off the list, or the right side of it.
folr f emptyresult [1,2,3,4]
turns intof(1, f(2, f(3, f(4, emptyresult) ) ) )
. Now just follow parenthesis in evaluation and that's it.One important thing to notice is that the supplied function
f
must handle its own return value as its second argument which implies both must have the same type.Source: my post where I look at it from an imperative uncurried javascript perspective if you think it might help.
此 wiki 页面中的图像可视化了
foldr
(以及foldl
)的概念:例如,
foldr (-) 0 的结果[1,2,3]
是 2,可以形象化为:即(从下到上):
所以
foldr (\xy -> (x+y)/2) 54 [12, 4, 10, 6]
通过以下方式计算:The images in this wiki page visualize the idea of
foldr
(andfoldl
also):For example, the result of
foldr (-) 0 [1,2,3]
is 2. It can be visualized as:That is (from bottom to the top):
So
foldr (\x y -> (x+y)/2) 54 [12, 4, 10, 6]
is being computed through: