Haskell 是否在懒惰评估期间丢弃中间结果?
Does Haskell discards intermediary results during lazy evaluation?
如果我递归定义斐波那契数列:
fibo_lazy_list = 0 : 1 : zipWith (+) fibo_lazy_list (tail fibo_lazy_list)
然后要求第一个大于给定值的元素,比如:
print $ find (>100) fibo_lazy_list
我了解 Haskell 仅评估获得打印结果所需的元素。但是在打印之前它们是否都保存在内存中?由于只需要列表中的两个元素来计算最后一个元素,Haskell 是释放最左边的元素还是列表在内存中不断增长?
视情况而定。
这实际上是正确处理真实世界代码的最棘手的事情之一 Haskell:为了避免因保留不必要的数据而导致的内存泄漏,这本来应该是中间的,但事实证明实际上是对一些尚未评估的惰性 thunk 的依赖,因此不能被垃圾收集。
在您的示例中,fibo_lazy_list
的前导元素(顺便说一句,请使用 camelCase
,而不是 Haskell 中的 underscore_case
),只要因为 fibo_lazy_list
被仍然可以评估的东西所引用。但是一旦它超出范围,那是不可能的。所以如果你这样写
print $ let fibo_lazy_list = 0 : 1 : zipWith (+) fibo_lazy_list (tail fibo_lazy_list)
in find (>100) fibo_lazy_list
那么您可以非常确信未使用的元素 将被垃圾收集,甚至可能在找到要打印的元素之前。
但是,如果 fibo_lazy_list
是在顶层定义的,并且是 CAF(如果类型不是多态的话)
fiboLazyList :: [Integer]
fiboLazyList = 0 : 1 : zipWith (+) fiboLazyList (tail fiboLazyList)
main :: IO ()
main = do
...
print $ find (>100) fiboLazyList
...
那么你最好期望所有前导元素即使在 >100
被提取后仍保留在内存中。
编译器优化可能在这里有用,严格注释也可以。但正如我所说,这在 Haskell 中有点痛苦。
如果我递归定义斐波那契数列:
fibo_lazy_list = 0 : 1 : zipWith (+) fibo_lazy_list (tail fibo_lazy_list)
然后要求第一个大于给定值的元素,比如:
print $ find (>100) fibo_lazy_list
我了解 Haskell 仅评估获得打印结果所需的元素。但是在打印之前它们是否都保存在内存中?由于只需要列表中的两个元素来计算最后一个元素,Haskell 是释放最左边的元素还是列表在内存中不断增长?
视情况而定。
这实际上是正确处理真实世界代码的最棘手的事情之一 Haskell:为了避免因保留不必要的数据而导致的内存泄漏,这本来应该是中间的,但事实证明实际上是对一些尚未评估的惰性 thunk 的依赖,因此不能被垃圾收集。
在您的示例中,fibo_lazy_list
的前导元素(顺便说一句,请使用 camelCase
,而不是 Haskell 中的 underscore_case
),只要因为 fibo_lazy_list
被仍然可以评估的东西所引用。但是一旦它超出范围,那是不可能的。所以如果你这样写
print $ let fibo_lazy_list = 0 : 1 : zipWith (+) fibo_lazy_list (tail fibo_lazy_list)
in find (>100) fibo_lazy_list
那么您可以非常确信未使用的元素 将被垃圾收集,甚至可能在找到要打印的元素之前。
但是,如果 fibo_lazy_list
是在顶层定义的,并且是 CAF(如果类型不是多态的话)
fiboLazyList :: [Integer]
fiboLazyList = 0 : 1 : zipWith (+) fiboLazyList (tail fiboLazyList)
main :: IO ()
main = do
...
print $ find (>100) fiboLazyList
...
那么你最好期望所有前导元素即使在 >100
被提取后仍保留在内存中。
编译器优化可能在这里有用,严格注释也可以。但正如我所说,这在 Haskell 中有点痛苦。