foldl懒惰怎么样?

时间:2011-09-20 15:08:25

标签: haskell lazy-evaluation fold

Haskell中有很多关于foldlfoldrfoldl'的{​​{3}}。

所以现在我知道:
1)foldl是懒惰的 2)不要使用foldl,因为它可能会炸毁堆栈 3)使用foldl'代替,因为它是严格的(good questions and answers

如何评估foldl
1)创建了一大堆thunks 2)在Haskell完成创建thunk后,thunk减少了 3)如果有太多的thunk,则溢出堆栈

我很困惑:
1)为什么减少必须在所有thunking之后发生?
2)为什么foldl不像foldl'一样进行评估?这只是一个实施副作用吗? 3)来自ishfoldl看起来可以使用尾递归有效地评估 - 我如何判断一个函数是否真的会被有效地评估?如果我不想让我的程序崩溃,似乎我必须开始担心Haskell中的评估顺序。

提前致谢。我不知道我对foldl评估的理解是否正确 - 如有必要,请提出更正建议。


更新:看起来我的问题的答案与Normal Form,Weak Normal Form和Head Normal Form以及Haskell的实现有关。
但是,我仍然在寻找一个例子,更加热切地评估组合函数会导致不同的结果(崩溃或不必要的评估)。

4 个答案:

答案 0 :(得分:8)

简短的回答是,在foldl f中,f并不一定是严格的,所以它可能过于急于减少前面的风险。但是,在实践中通常是这样,因此您几乎总是希望使用foldl'

我写了a more in-depth explanation of how the evaluation order of foldl and foldl' works on another question。它相当长,但我认为它应该为你澄清一些事情。

答案 1 :(得分:4)

你知道,根据定义:

foldl op start (x1:x2:...:xN:[]) = ((start `op` x1) `op` x2) ...

foldl中执行此操作的行是:

foldl op a (x:xs) = foldl op (a `op` x) xs

你是对的,这是尾递归,但观察表达式

(a `op` x)

是懒惰的,在列表的末尾,将构建一个巨大的表达式,然后减少。 foldl'的区别只是上面的表达式被强制在每次递归中进行求值,所以最后你得到一个弱头正常形式的值。

答案 2 :(得分:1)

  

我还在寻找一个例子,更加热切地评估组合功能会导致不同的结果

一般的经验法则永远不会使用foldl。除非您使用foldl',否则请始终使用foldr我认为您对foldl了解为何应该避免这一点已经足够了解。

另请参阅:Real World Haskell > Functional Programming # Left folds, laziness, and space leaks

但是,您的问题忽略了foldr。关于foldr的好消息是它可以产生增量结果,而foldl'需要在产生答案之前遍历整个列表。这意味着foldr的懒惰允许它使用无限列表。关于这类事情也有详细的问题和答案。

提起之后,让我试着简洁地回答你的问题。

  

1)为什么减少必须在所有thunking之后发生?

仅限 在严格点发生。例如,执行IO是一个严格的要点。 foldl'使用seq添加foldl没有的额外严格点。

  

2)为什么foldl不像foldl那样进行评估?这只是一个实施副作用吗?

由于foldl'

中的额外严格性点
  

3)从定义来看,foldl对我来说看起来像一个尾递归函数 - 我如何判断一个函数是否真的会被有效地评估?如果我不想让我的程序崩溃,似乎我必须开始担心Haskell中的评估顺序。

您需要更多地了解延迟评估。延迟评估并不是Haskell独有的,但Haskell是极少数语言中的一种,其中懒惰是默认的。对于初学者,请记住始终使用foldl',你应该没事。

如果懒惰实际上有一天让你陷入困境,那就是你应该确保你理解懒惰和Haskell的严格要点。你可能会说理论日是懒惰学习的严格点。

另请参阅:PLAI > Part III: Laziness

答案 3 :(得分:0)

  

如果我不想让我的程序崩溃,似乎我必须开始担心Haskell中的评估顺序。

在我看来,如果你希望你的程序不会崩溃,那么最好的选择是(按照每次花费的收益率排列越来越差): 1.给它足够的资源。 2.改进您的算法。 3.进行微观优化(其中一个是foldl')。

所以,而不是担心评估的顺序,我宁愿首先担心要评估什么foldr是否足够?我可以吗?根本不折叠吗?)。在此之前,我会增加可用的堆栈空间。

您不会将整个程序限制为8MB RAM,对吗?为什么要限制堆栈空间呢?只需将堆栈空间增加到4GB,并开始担心什么东西真的很麻烦(就像堆内存一样)。

并在某种程度上回答了foldl如何懒惰的问题:

foldl (\x y -> y) undefined [undefined, 8] -- evaluates to 8
foldl' (\x y -> y) undefined [undefined, 8] -- fails to evaluate