Foldr 与 Foldl(或 Foldl')的含义

发布于 2024-07-10 20:02:50 字数 394 浏览 4 评论 0 原文

首先,我正在阅读的Real World Haskell说永远不要使用foldl,而是使用foldl'。 所以我相信它。

但我不清楚何时使用 foldrfoldl'。 尽管我可以看到它们以不同方式工作的结构摆在我面前,但我太愚蠢了,无法理解什么时候“哪个更好”。 我想在我看来,使用哪个并不重要,因为它们都会产生相同的答案(不是吗?)。 事实上,我之前对这个构造的经验来自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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

东风软 2024-07-17 20:02:50

foldr fx ys 的递归,其中 ys = [y1,y2,...,yk] 看起来像,

f y1 (f y2 (... (f yk x) ...))

foldl fx ys 的递归看起来

f (... (f (f x y1) y2) ...) yk

这里的一个重要区别是,如果可以仅使用 x 的值来计算 fx y 的结果,那么 foldr 则不能' 需要检查整个列表。 例如,

foldr (&&) False (repeat False)

返回 False

foldl (&&) False (repeat False)

永远不会终止。 (注意:repeat False 创建一个无限列表,其中每个元素都是 False。)

另一方面,foldl' 是尾递归且严格的。 如果您知道无论如何都必须遍历整个列表(例如,对列表中的数字求和),那么 foldl'foldl' 具有更高的空间(可能还有时间)效率。代码>文件夹

The recursion for foldr f x ys where ys = [y1,y2,...,yk] looks like

f y1 (f y2 (... (f yk x) ...))

whereas the recursion for foldl f x ys looks like

f (... (f (f x y1) y2) ...) yk

An important difference here is that if the result of f x y can be computed using only the value of x, then foldr doesn't' need to examine the entire list. For example

foldr (&&) False (repeat False)

returns False whereas

foldl (&&) False (repeat False)

never terminates. (Note: repeat False creates an infinite list where every element is False.)

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), then foldl' is more space- (and probably time-) efficient than foldr.

锦上情书 2024-07-17 20:02:50

foldr 看起来像这样:

右折叠可视化

foldl 看起来像这个:

上下文:折叠

foldr looks like this:

Right-fold visualization

foldl looks like this:

Left-fold visualization

Context: Fold on the Haskell wiki

别闹i 2024-07-17 20:02:50

它们的语义不同,因此您不能简单地互换 foldlfoldr。 一个从左侧向上折叠元素,另一个从右侧向上折叠。 这样,运算符就会以不同的顺序应用。 这对于所有非关联运算(例如减法)都很重要。

Haskell.org 有一个有趣的

Their semantics differ so you can't just interchange foldl and foldr. 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.

浮光之海 2024-07-17 20:02:50

不久之后,当累加器函数对其第二个参数惰性时,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).

小嗷兮 2024-07-17 20:02:50

在 99% 的所有用途中,foldl' 优于 foldl 的原因是,对于大多数用途,它可以在恒定空间中运行。

采用函数 sum = Foldl['] (+) 0。 当使用 foldl' 时,会立即计算总和,因此将 sum 应用于无限列表将永远运行,并且很可能在恒定空间中(如果您使用如果数字是 IntDoubleFloat 等,那么 Integer 将使用比常量更大的空间。变得大于 maxBound :: Int )。

使用foldl,构建了一个thunk(就像如何获取答案的秘诀,可以稍后对其进行评估,而不是存储答案)。 这些 thunk 可能会占用大量空间,在这种情况下,评估表达式比存储 thunk 要好得多(导致堆栈溢出......并导致你......哦没关系)

希望有所帮助。

The reason foldl' is preferred to foldl for 99% of all uses is that it can run in constant space for most uses.

Take the function sum = foldl['] (+) 0. When foldl' is used, the sum is immediately calculated, so applying sum to an infinite list will just run forever, and most likely in constant space (if you’re using things like Ints, Doubles, Floats. Integers will use more than constant space if the number becomes larger than maxBound :: 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.

阳光的暖冬 2024-07-17 20:02:50

顺便说一句,Ruby 的 inject 和 Clojure 的 reducefoldl (或 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's reduce are foldl (or foldl1, depending on which version you use). Usually, when there is only one form in a language, it is a left fold, including Python's reduce, Perl's List::Util::reduce, C++'s accumulate, C#'s Aggregate, Smalltalk's inject:into:, PHP's array_reduce, Mathematica's Fold, etc. Common Lisp's reduce defaults to left fold but there's an option for right fold.

轻拂→两袖风尘 2024-07-17 20:02:50

正如 Konrad 指出的,它们的语义是不同的。 它们甚至没有相同的类型:

ghci> :t foldr
foldr :: (a -> b -> b) -> b -> [a] -> b
ghci> :t foldl
foldl :: (a -> b -> a) -> a -> [b] -> a
ghci> 

例如,列表追加运算符 (++) 可以使用 foldr 实现,因为

(++) = flip (foldr (:))

while

(++) = flip (foldl (:))

会给出类型错误。

As Konrad points out, their semantics are different. They don't even have the same type:

ghci> :t foldr
foldr :: (a -> b -> b) -> b -> [a] -> b
ghci> :t foldl
foldl :: (a -> b -> a) -> a -> [b] -> a
ghci> 

For example, the list append operator (++) can be implemented with foldr as

(++) = flip (foldr (:))

while

(++) = flip (foldl (:))

will give you a type error.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文