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)
在这些情况下,我知道foldl并foldl'做我想做的事。我更感兴趣的是了解这是如何实现的。
你是对的。
在操作上,在情况 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 时打印调试消息。这不用于一般输出,但用于一般调试很好。