vku*_*cki 6 haskell lazy-evaluation lazy-sequences
如果我递归地定义斐波那契数列:
fibo_lazy_list = 0 : 1 : zipWith (+) fibo_lazy_list (tail fibo_lazy_list)
Run Code Online (Sandbox Code Playgroud)
然后询问给定值上方的第一个元素,例如:
print $ find (>100) fibo_lazy_list
Run Code Online (Sandbox Code Playgroud)
我知道 Haskell 只评估获得打印结果所需的元素。但是它们是否都保存在内存中直到打印出来?由于只需要列表的两个元素来计算最后一个元素,Haskell 是释放最左边的元素还是列表在内存中不断增长?
lef*_*out 10
这取决于。
这实际上是现实世界 Haskell 代码中最棘手的事情之一:为了避免因持有不必要的数据而导致的内存泄漏,这只是应该是中间的,但结果实际上是对某些尚未的依赖 -未评估的懒惰 thunk,因此不能被垃圾收集。
在您的示例中,fibo_lazy_list(顺便说一句,请使用camelCase,而不是underscore_case在 Haskell 中)的主要元素不会被垃圾收集,只要它fibo_lazy_list被仍然可以评估的东西引用。但是一旦超出范围,那是不可能的。所以如果你这样写
print $ let fibo_lazy_list = 0 : 1 : zipWith (+) fibo_lazy_list (tail fibo_lazy_list)
in find (>100) fibo_lazy_list
Run Code Online (Sandbox Code Playgroud)
那么您可以非常确信未使用的元素将被垃圾收集,甚至可能在找到要打印的元素之前。
但是,如果fibo_lazy_list在顶级定义,并且是CAF(如果类型不是多态的,则为)
fiboLazyList :: [Integer]
fiboLazyList = 0 : 1 : zipWith (+) fiboLazyList (tail fiboLazyList)
main :: IO ()
main = do
...
print $ find (>100) fiboLazyList
...
Run Code Online (Sandbox Code Playgroud)
那么您应该更好地期望所有主要元素即使在>100提取后仍保留在内存中。
编译器优化在这里可能会有所帮助,严格注释也是如此。但正如我所说,这在 Haskell 中有点痛苦。