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到无限的名单将只是在不断的空间一直运行下去,而且极有可能(如果你使用喜欢的东西IntS,Doubles和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>
例如,列表附加运算符(++)可以用foldras 实现
(++) = 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而foldris是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(正)。