16

今天,当我在编写一个小脚本时,我使用foldlfoldl'. 我得到了stack overflow,所以我导入Data.List (foldl')并对此感到满意。这是我的默认工作流程foldl。只foldl'在懒惰的版本落下评估时使用。

Real World Haskell说我们应该在大多数情况下使用foldl'而不是。Foldr Foldl Foldl'foldl

通常选择在foldr和之间foldl'

...

但是,如果组合函数的第一个参数是惰性的,foldl则可能会愉快地返回foldl'遇到异常的结果。

和一个给定的例子:

(?) :: Int -> Int -> Int
_ ? 0 = 0
x ? y = x*y

list :: [Int]
list = [2, 3, undefined, 5, 0]

okey = foldl (?) 1 list

boom = foldl' (?) 1 list

好吧,我很抱歉,但这是相当学术的,有趣但学术的例子。所以我问,有没有实际使用的例子foldl?我的意思是,当我们无法替换foldlfoldl'.

PS 我知道,很难定义 term practical,但我希望你能理解我的意思。

PPS我明白了,为什么懒惰foldl是haskell的默认设置。我不要求任何人搬山并默认使用严格的版本。我只是对foldl函数独占使用的例子很感兴趣:)

PPPS 好吧,foldl欢迎任何有趣的用法。

4

2 回答 2

13

这是一个更实际的例子,它使用经典的朴素斐波那契实现来模拟昂贵的计算:

fib :: Int -> Int
fib 0 = 1
fib 1 = 1
fib n = fib (n - 1) + fib (n - 2)

f :: Int -> Int -> Int
f a b = if b < 1000 then b else min b a

那么如果你有

> -- Turn on statistics for illustrative purposes
> :set +s
> foldl f maxBound $ map fib [30, 20, 15]
987
(0.02 secs, 0 bytes)
> foldl' f maxBound $ map fib [30, 20, 15]
987
(4.54 secs, 409778880 bytes)

在这里,惰性版本和严格版本之间的运行时性能存在巨大差异,惰性版本以压倒性优势胜出。当然,您的数字可能因您的计算机而异,但您肯定会注意到执行速度的差异。foldl'强制每个计算发生,而没有foldl。这对类似的东西也很有用

> foldl f maxBound $ map length [repeat 1, repeat 1, replicate 10 1]
10

fib示例不同,此计算在技术上涉及底部,因为length $ repeat 1永远不会完成其计算。通过不让两个参数都f严格(就像foldl'那样),我们实际上有一个停止的程序与一个永远不会停止的程序。

于 2014-09-19T21:18:29.230 回答
5

我可以想到一个(尽管这可能是只有使用优化编译器才能产生好的代码):

last = foldl (\_ x -> x) (error "emptyList")

它不会有正确的行为foldl'

> foldl (\_ x -> x) (error "emptyList") [error "foo", "last"]
"last"
> foldl' (\_ x -> x) (error "emptyList") [error "foo", "last"]
"*** Exception: foo
于 2014-09-19T20:51:26.170 回答