foldl如何懒惰?

在Haskell中有关于foldlfoldrfoldl'很多很好的问题和答案。

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

如何评估foldl
1)创建了一大堆thunk
2)在Haskell创建thunk之后,thunk被减少
3)如果太多的thunk,堆溢出

我感到困惑的是:
1)为什么减少必须发生在所有thunk-ing之后?
2)为什么不foldl评估就像foldl' ? 这只是一个实现的副作用?
3)根据定义, foldl看起来可以使用尾递归有效地进行评估 - 我如何判断一个函数是否会被有效地评估? 看起来我不得不开始担心Haskell的评估顺序,如果我不想让我的程序崩溃的话。

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


更新:它看起来对我的问题的答案与正常形式,弱正常形式和正常形式,以及哈斯克尔的执行情况有关。
然而,我仍然在寻找一个例子,更为热切地评估组合函数会导致不同的结果(崩溃或不必要的评估)。


你知道,按照定义:

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'的不同之处在于上面的表达式被迫在每次递归中进行评估,所以最终你在弱头标准形式下获得了一个值。


简短的回答是,在foldl f ,它不一定,此案f是严格的,所以它可能是太急于减少的thunk前面。 但是,实际上它通常是,所以你几乎总是想要使用foldl'

我写了关于foldlfoldl'的评估顺序如何在另一个问题上工作的更深入的解释。 这很长,但我认为它应该为你澄清一些事情。


我仍然在寻找一个更加热切地评估组合函数会导致不同结果的例子

一般的经验法则永远不会使用foldl 。 总是使用foldl'除非你使用foldr 我认为你对foldl了解得很多,理解为什么应该避免。

另请参见:真实世界Haskell>函数式编程#左折叠,懒惰和空间泄漏

但是,你的问题忽略了foldr 。 关于foldr处在于它可以产生增量结果,而foldl'需要在得到答案之前遍历整个列表。 这意味着foldr的懒惰允许它与无限列表一起工作。 还有关于这类事情的详细问题和答案。

介绍完这些后,让我试着简洁地回答你的问题。

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

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

2)为什么不像foldl'那样评估foldl? 这只是一个实现的副作用?

由于foldl'中的额外严格点,

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

您需要更多地了解懒惰评估。 懒惰评估并不是Haskell独有的,但Haskell是默认为懒惰的极少数语言之一。 对于初学者,只记得总是使用foldl' ,你应该没问题。

如果有一天懒惰真的让你陷入麻烦,那么你应该确保你明白懒惰和Haskell的严格要求。 你可能会说,理论日是懒惰学习的严格要求。

另见:PLAI>第三部分:懒惰

链接地址: http://www.djcxy.com/p/42973.html

上一篇: How is foldl lazy?

下一篇: Haskell $! operator and infinite lists