Haskell懒惰和序列

时间:2018-11-12 22:57:35

标签: haskell lazy-evaluation

我试图理解Haskell中的懒惰和序列。

在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

在这些情况下,我知道foldl和foldl'是我想要的。我对了解如何实现这一目标更感兴趣。

1 个答案:

答案 0 :(得分:3)

是的,你是对的。

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

在情况2中,变量v始终绑定到评估的数字。递归在恒定空间中运行。

顺便说一句,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,因此即使它是重击,也要先进行评估。这也确保了恒定的内存占用。

作为严格的性能分析工具,您可以尝试Debug.Trace.trace,它会在需要重击时打印调试消息。请勿将其用于常规输出,但可以用于常规调试。