Haskell 懒惰和 seq

Haskell laziness and seq

我正在努力理解 Haskell 中的懒惰和 seq:


main = do
  f [1, 2, 3, 4] 0
  f' [1, 2, 3, 4] 0

g x = 42 * x -- this could be whatever

-- 1. lazy
f [] v = print v -- base case
f (x:xs) v = f xs v'
  where v' = v + g x

-- 2. strict
f' [] v = print v -- base case
f' (x:xs) v = seq v' f' xs v'
  where v' = v + g x

我知道 foldlfoldl' 在这些情况下会做我想做的事。我更有兴趣了解这是如何实现的。

是的,你是对的。

在操作上,在情况 1 中,变量 v 绑定到一个 thunk,一个未评估的表达式,它变得越来越大,直到 print 强制其评估。内存占用不是恒定的。

在情况 2 中,变量 v 总是绑定到一个评估的数字。递归以常量 space.

运行

顺便说一句,seq v' f' xs v'写成

是惯用的
v' `seq` f' xs v'

(这是等价的,因为应用程序总是严格执行函数)或者,利用 $!

f' xs $! v'

另一个常见的选择是使用刘海图案而忘记 seq

f' [] !v = print v -- base case
f' (x:xs) !v = f' xs v'
  where v' = v + g x

刘海!确保v立即被要求,所以即使它是一个thunk,它也会在继续之前被评估。这也确保了恒定的内存占用。

作为严格的分析工具,您可以尝试 Debug.Trace.trace,它会在需要某些 thunk 时打印调试消息。这不是用于一般输出,但用于一般调试是可以的。