【发布时间】:2011-09-21 07:18:08
【问题描述】:
如果我需要获取包含内存密集型元素的大型列表的值,我对 GHC 引发堆栈溢出感到有点惊讶。 我确实希望 GHC 有 TCO,所以我永远不会遇到这种情况。
为了最简化案例,请查看以下返回斐波那契数的函数的简单实现(取自 HaskellWiki)。目标是显示第百万个数字。
import Data.List
# elegant recursive definition
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
# a bit tricky using unfoldr from Data.List
fibs' = unfoldr (\(a,b) -> Just (a,(b,a+b))) (0,1)
# version using iterate
fibs'' = map fst $ iterate (\(a,b) -> (b,a+b)) (0,1)
# calculate number by definition
fib_at 0 = 0
fib_at 1 = 1
fib_at n = fib_at (n-1) + fib_at (n-2)
main = do
{-- All following expressions abort with
Stack space overflow: current size 8388608 bytes.
Use `+RTS -Ksize -RTS' to increase it.
--}
print $ fibs !! (10^6)
print . last $ take (10^6) fibs
print $ fibs' !! (10^6)
print $ fibs'' !! (10^6)
-- following expression does not finish after several
-- minutes
print $ fib_at (10^6)
源码用ghc -O2编译。
我做错了什么?我想避免使用增加的堆栈大小或其他特定的编译器选项进行重新编译。
【问题讨论】:
-
@David:阅读此内容以澄清 Haskell 中的 TCO:haskell.org/pipermail/haskell-cafe/2009-March/058607.html
-
fibs !! (10^6)...这是一个非常大的数字。 -
三个查找中的每一个都应该在几乎恒定的堆空间中工作 - 但是,您提到
fibs两次会导致它被保留。而您的fib_at版本具有指数级复杂性。再多的尾调用优化也无济于事...... -
@camccann> 确实如此。 120719474515914100282640417445683502254279809033418884477131589123371500059867801 ... 6074509262836314172778883659535102587911419872025962380687123573280832406684390626(208988位)。使用类似函数式的 Ruby 代码:Enumerator.new { |y| a,b=0,1;循环{y.yield a; a,b=b,a+b} }.with_index {|elt,ix|如果 ix == 10**6-1 然后打印 elt;休息;结束}
-
@David:我得到了不同的结果,1953282128707757731632.... 这似乎与 wolfram alpha 一致。试试我的代码并告诉我。
标签: haskell memory-leaks memory-management stack-overflow