【问题标题】:Practical use of `foldl``foldl` 的实际使用
【发布时间】:2014-09-19 20:42:00
【问题描述】:

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

Real World Haskell 表示在大多数情况下我们应该使用foldl' 而不是foldlFoldr Foldl Foldl' 这么说

通常在foldrfoldl' 之间进行选择。

...

但是,如果组合函数的第一个参数是惰性的,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的实际使用例子?我的意思是,当我们不能用foldl' 替换foldl 时。

P。 S. 我知道,很难定义术语 practical,但我希望你能明白我的意思。

P。 PS我明白了,为什么懒惰foldl在haskell中是默认的。我不要求任何人搬山并默认使用严格的版本。我只是对foldl 函数的独占使用示例非常感兴趣:)

P。 P.P.S. 好吧,欢迎使用 foldl 的任何有趣用法。

【问题讨论】:

  • 除了避免undefined,您还可以使用foldl 来避免潜在的昂贵操作。如果您的折叠函数在成功操作时提前退出,那么您可以避免通过惰性执行昂贵的计算。可能有更清晰的方法来编写它(想到monoids),但折叠对于编译器优化可能很好。
  • 是的,我正在考虑这个问题。但无法想象这样一个功能的好例子。

标签: haskell fold foldleft


【解决方案1】:

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

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' 所做的那样),我们实际上有一个程序会停止而不是一个永远不会停止的程序。

【讨论】:

  • 使用reverse 后跟foldr 不是更好吗?这样您也不必处理整个列表。
  • @TomEllis 我并不是说这是最好的方法,只是它是 foldl 如何成为更好选择的一个例子。
  • 嗯,谢谢你的回答。现在我有一些使用foldl 的有趣示例:D
  • @TomEllis reverse 处理整个列表。
  • @JeremyList 是的。我不记得我在想什么!
【解决方案2】:

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

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

【讨论】:

  • 嗯,这真的很像_ ? 0 = 0。但无论如何,谢谢你再举一个例子。
猜你喜欢
  • 2016-09-04
  • 1970-01-01
  • 2019-08-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-04
  • 2010-09-27
  • 1970-01-01
相关资源
最近更新 更多