我试图理解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'是我想要的。我对了解如何实现这一目标更感兴趣。
答案 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
,它会在需要重击时打印调试消息。请勿将其用于常规输出,但可以用于常规调试。