为什么(sum $ takeWhile(< 10000000)[1 ..])使用这么多内存?

时间:2013-01-12 23:10:45

标签: haskell

这是我在控制台中获得的内容:

ghci> sum $ takeWhile (<10000000) [1..]
49999995000000
(11.96 secs, 2174569400 bytes)

超过2GB!我想,sum可以丢弃它已经总结的任何东西。你会怎么写这个?

2 个答案:

答案 0 :(得分:12)

你创造了一千万Integer个,以及很多列表单元格。此外,如果您通过编译器运行解释代码,则会运行解释代码,这会在一定程度上减少分配。

主要问题是解释器根本没有优化,因此sum使用构建巨大 thunk的惰性变体。 sum丢弃它已经消耗的列表部分,但它会用thunk替换它来计算结果,所以

sum [1,2,3,4 ...]

变为

(...((((0 + 1) + 2) + 3) + 4) + ...)

之后。这不是最佳替代,因为Integer的加法是严格的。

在ghci提示符下,您应该写

Prelude Data.List> foldl' (+) 0 $ takeWhile (< 10000000) [1 .. ]
49999995000000
(1.41 secs, 1443355832 bytes)

解决这个问题。在编译(当然还有优化)程序中,sum可以正常工作。

答案 1 :(得分:5)

这看起来与http://www.haskell.org/haskellwiki/Memory_leak

中描述的完全相同

因此,此处的解决方案可能是foldl' (+) 0 $ takeWhile (<10000000) [1..](需要import Data.List)。

可能有一个更好的解决方案,因为我只是一个Haskell新手并且也很好奇。 = ^。^ = (编辑:请阅读以下第一条评论:-P)