Haskell中有很多关于foldl
,foldr
和foldl'
的{{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)来自ish,foldl
看起来可以使用尾递归有效地评估 - 我如何判断一个函数是否真的会被有效地评估?如果我不想让我的程序崩溃,似乎我必须开始担心Haskell中的评估顺序。
提前致谢。我不知道我对foldl
评估的理解是否正确 - 如有必要,请提出更正建议。
更新:看起来我的问题的答案与Normal Form,Weak Normal Form和Head Normal Form以及Haskell的实现有关。
但是,我仍然在寻找一个例子,更加热切地评估组合函数会导致不同的结果(崩溃或不必要的评估)。
答案 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的严格要点。你可能会说理论日是懒惰学习的严格点。
答案 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