Answers:
递归在foldr f x ys
哪里ys = [y1,y2,...,yk]
看起来像
f y1 (f y2 (... (f yk x) ...))
而递归foldl f x ys
看起来像
f (... (f (f x y1) y2) ...) yk
这里的一个重要区别是,如果f x y
可以仅使用的值来计算的结果x
,则foldr
无需检查整个列表。例如
foldr (&&) False (repeat False)
返回False
而
foldl (&&) False (repeat False)
永不终止。(注意:repeat False
创建一个无限列表,其中每个元素都是False
。)
另一方面,foldl'
是尾递归和严格的。如果您知道无论如何都要遍历整个列表(例如,对列表中的数字求和),则foldl'
比()可能更节省空间(并且可能更节省时间)foldr
。
foldl
,,它会收集其整体结果并仅在所有工作完成并且没有更多要执行的步骤之后才产生它。因此,例如foldl (flip (:)) [] [1..3] == [3,2,1]
,这样scanl (flip(:)) [] [1..] = [[],[1],[2,1],[3,2,1],...]
... IOW foldl f z xs = last (scanl f z xs)
和无限列表没有最后一个元素(在上面的示例中,它本身就是一个无限列表,从INF到1)。
它们的语义不同,因此您不能仅仅互换foldl
和foldr
。一个将元素从左侧向上折叠,另一个从右侧折叠。这样,操作员将以不同的顺序应用。这对于所有非关联运算(例如减法)都很重要。
Haskell.org上有一篇有趣的文章。
之所以对所有用途的99%foldl'
首选,foldl
是因为对于大多数用途,它可以在恒定的空间中运行。
采取功能sum = foldl['] (+) 0
。当foldl'
被使用时,总和立即计算,因此应用sum
到无限的名单将只是在不断的空间一直运行下去,而且极有可能(如果你使用喜欢的东西Int
S,Double
s和Float
秒。Integer
旨意使用超过如果常量空间数字变得大于maxBound :: Int
)。
使用foldl
,可以建立一个thunk(就像如何获得答案的菜谱,可以稍后对其进行评估,而不是存储答案)。这些thunk会占用很多空间,在这种情况下,评估表达式比存储thunk更好(导致堆栈溢出……导致您……哦,没关系)
希望有帮助。
foldl
除了将构造函数应用于其一个或多个参数之外,什么都不做。
foldl
才是最佳选择是否有通用的模式?(就像无限列表一样,什么时候foldr
是错误的选择,优化方式??)
顺便说一下,Ruby inject
和Clojure reduce
是foldl
(或foldl1
,取决于您使用的版本)。通常,当一种语言中只有一种形式时,它是左折的,包括Python的reduce
,Perl的List::Util::reduce
,C ++的accumulate
,C#的Aggregate
,Smalltalk的inject:into:
,PHP的array_reduce
,Mathematica的Fold
等。CommonLisp的reduce
默认值是左折,但是可以选择右折。
reduce
并不懒惰,所以它是懒惰的,这里的foldl'
许多注意事项都不适用。
foldl'
,因为它们是严格的语言,不是吗?否则,这是否意味着所有这些版本都会导致堆栈溢出foldl
?
正如Konrad所指出的,它们的语义是不同的。它们甚至没有相同的类型:
ghci> :t foldr
foldr :: (a -> b -> b) -> b -> [a] -> b
ghci> :t foldl
foldl :: (a -> b -> a) -> a -> [b] -> a
ghci>
例如,列表附加运算符(++)可以用foldr
as 实现
(++) = flip (foldr (:))
而
(++) = flip (foldl (:))
将给您输入错误。
foldl subtract 0 [1, 2, 3, 4]
计算为-10
,而foldr subtract 0 [1, 2, 3, 4]
计算为-2
。foldl
实际上是,0 - 1 - 2 - 3 - 4
而foldr
is是4 - 3 - 2 - 1 - 0
。
foldr (-) 0 [1, 2, 3, 4]
是-2
和foldl (-) 0 [1, 2, 3, 4]
是-10
。在另一方面,subtract
是从向后你可能会想到什么(subtract 10 14
是4
),所以foldr subtract 0 [1, 2, 3, 4]
是-10
和foldl subtract 0 [1, 2, 3, 4]
是2
(正)。