Haskell 懒惰和 seq

Pau*_*aul 4 haskell lazy-evaluation

我试图理解懒惰和seqHaskell:

  • 在(1)中,v在基本情况下的打印需要之前不进行评估是否正确v

  • 在(2)中,v'在每次递归调用之前评估是否正确,以便v在基本情况下不需要评估?如果没有,我如何实施这种严格的评估?

  • 我可以使用任何分析工具来为自己确认这两点吗?


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
Run Code Online (Sandbox Code Playgroud)

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

chi*_*chi 6

你是对的。

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

在第 2 种情况下,变量v总是绑定到一个评估数字。递归在恒定空间中运行。

顺便说一句,惯用的写法seq v' f' xs v'

v' `seq` f' xs v'
Run Code Online (Sandbox Code Playgroud)

(这是等价的,因为应用程序总是对功能很严格)或者,利用 $!

f' xs $! v'
Run Code Online (Sandbox Code Playgroud)

另一个常见的选择是使用 bang 模式并忘记 seq

f' [] !v = print v -- base case
f' (x:xs) !v = f' xs v'
  where v' = v + g x
Run Code Online (Sandbox Code Playgroud)

刘海!确保v立即需要,因此即使是重击,也会在继续之前进行评估。这也确保了恒定的内存占用。

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