首先,我正在阅读的Real World Haskell说永远不要使用foldl
,而是使用foldl'
。 所以我相信它。
但我不清楚何时使用 foldr
和 foldl'
。 尽管我可以看到它们以不同方式工作的结构摆在我面前,但我太愚蠢了,无法理解什么时候“哪个更好”。 我想在我看来,使用哪个并不重要,因为它们都会产生相同的答案(不是吗?)。 事实上,我之前对这个构造的经验来自Ruby的inject
和Clojure的reduce
,它们似乎没有“左”和“右”版本。 (附带问题:他们使用哪个版本?)
任何可以帮助像我这样的聪明人的见解将不胜感激!
Firstly, Real World Haskell, which I am reading, says to never use foldl
and instead use foldl'
. So I trust it.
But I'm hazy on when to use foldr
vs. foldl'
. Though I can see the structure of how they work differently laid out in front of me, I'm too stupid to understand when "which is better." I guess it seems to me like it shouldn't really matter which is used, as they both produce the same answer (don't they?). In fact, my previous experience with this construct is from Ruby's inject
and Clojure's reduce
, which don't seem to have "left" and "right" versions. (Side question: which version do they use?)
Any insight that can help a smarts-challenged sort like me would be much appreciated!
发布评论
评论(7)
foldr fx ys
的递归,其中ys = [y1,y2,...,yk]
看起来像,而
foldl fx ys
的递归看起来这里的一个重要区别是,如果可以仅使用
x
的值来计算fx y
的结果,那么foldr
则不能' 需要检查整个列表。 例如,返回
False
而永远不会终止。 (注意:
repeat False
创建一个无限列表,其中每个元素都是False
。)另一方面,
foldl'
是尾递归且严格的。 如果您知道无论如何都必须遍历整个列表(例如,对列表中的数字求和),那么foldl'
比foldl'
具有更高的空间(可能还有时间)效率。代码>文件夹。The recursion for
foldr f x ys
whereys = [y1,y2,...,yk]
looks likewhereas the recursion for
foldl f x ys
looks likeAn important difference here is that if the result of
f x y
can be computed using only the value ofx
, thenfoldr
doesn't' need to examine the entire list. For examplereturns
False
whereasnever terminates. (Note:
repeat False
creates an infinite list where every element isFalse
.)On the other hand,
foldl'
is tail recursive and strict. If you know that you'll have to traverse the whole list no matter what (e.g., summing the numbers in a list), thenfoldl'
is more space- (and probably time-) efficient thanfoldr
.foldr
看起来像这样:foldl
看起来像这个:上下文:折叠
foldr
looks like this:foldl
looks like this:Context: Fold on the Haskell wiki
它们的语义不同,因此您不能简单地互换
foldl
和foldr
。 一个从左侧向上折叠元素,另一个从右侧向上折叠。 这样,运算符就会以不同的顺序应用。 这对于所有非关联运算(例如减法)都很重要。Haskell.org 有一个有趣的 。
Their semantics differ so you can't just interchange
foldl
andfoldr
. The one folds the elements up from the left, the other from the right. That way, the operator gets applied in a different order. This matters for all non-associative operations, such as subtraction.Haskell.org has an interesting article on the subject.
不久之后,当累加器函数对其第二个参数惰性时,foldr 会更好。 阅读 Haskell wiki 的 Stack Overflow(双关语)了解更多信息。
Shortly,
foldr
is better when the accumulator function is lazy on its second argument. Read more at Haskell wiki's Stack Overflow (pun intended).在 99% 的所有用途中,
foldl'
优于foldl
的原因是,对于大多数用途,它可以在恒定空间中运行。采用函数
sum = Foldl['] (+) 0
。 当使用foldl'
时,会立即计算总和,因此将sum
应用于无限列表将永远运行,并且很可能在恒定空间中(如果您使用如果数字是Int
、Double
、Float
等,那么Integer
将使用比常量更大的空间。变得大于 maxBound :: Int )。使用foldl,构建了一个thunk(就像如何获取答案的秘诀,可以稍后对其进行评估,而不是存储答案)。 这些 thunk 可能会占用大量空间,在这种情况下,评估表达式比存储 thunk 要好得多(导致堆栈溢出......并导致你......哦没关系)
希望有所帮助。
The reason
foldl'
is preferred tofoldl
for 99% of all uses is that it can run in constant space for most uses.Take the function
sum = foldl['] (+) 0
. Whenfoldl'
is used, the sum is immediately calculated, so applyingsum
to an infinite list will just run forever, and most likely in constant space (if you’re using things likeInt
s,Double
s,Float
s.Integer
s will use more than constant space if the number becomes larger thanmaxBound :: Int
).With
foldl
, a thunk is built up (like a recipe of how to get the answer, which can be evaluated later, rather than storing the answer). These thunks can take up a lot of space, and in this case, it’s much better to evaluate the expression than to store the thunk (leading to a stack overflow… and leading you to… oh never mind)Hope that helps.
顺便说一句,Ruby 的
inject
和 Clojure 的reduce
是foldl
(或foldl1
,具体取决于您使用的版本) 。 通常,当一种语言只有一种形式时,就是左折叠,包括Python的reduce
、Perl的List::Util::reduce
、C++的accumulate
、C# 的Aggregate
、Smalltalk 的inject:into:
、PHP 的array_reduce
、Mathematica 的Fold
等。 Common Lisp 的reduce
默认为左折叠,但有一个右折叠选项。By the way, Ruby's
inject
and Clojure'sreduce
arefoldl
(orfoldl1
, depending on which version you use). Usually, when there is only one form in a language, it is a left fold, including Python'sreduce
, Perl'sList::Util::reduce
, C++'saccumulate
, C#'sAggregate
, Smalltalk'sinject:into:
, PHP'sarray_reduce
, Mathematica'sFold
, etc. Common Lisp'sreduce
defaults to left fold but there's an option for right fold.正如 Konrad 指出的,它们的语义是不同的。 它们甚至没有相同的类型:
例如,列表追加运算符 (++) 可以使用
foldr
实现,因为while
会给出类型错误。
As Konrad points out, their semantics are different. They don't even have the same type:
For example, the list append operator (++) can be implemented with
foldr
aswhile
will give you a type error.