Haskell脚本耗尽空间

时间:2009-04-10 02:01:32

标签: haskell space lazy-evaluation

我正在使用项目Euler来教我自己的Haskell,而且我在解释我的代码是如何被haskell执行时遇到了一些麻烦。第二个问题让我计算所有偶数斐波那契数的总和高达400万。我的脚本看起来像这样:

fibs :: [Integer]
fibs =  1 : 2 : [ a+b | (a,b) <- zip fibs (tail fibs)]

evens  :: Integer -> Integer -> Integer
evens x sum | (even x)   = x + sum
            | otherwise  = sum

main = do
  print (foldr evens 0 (take 4000000 fibs))

Hugs给出错误“垃圾收集无法回收足够的空间”,我认为这意味着列表条目在foldr消耗时不会被释放。

我需要做些什么来解决这个问题?我尝试编写一个使用累加器的尾递归(我认为)版本,但也无法使用它。

5 个答案:

答案 0 :(得分:8)

首先,你不应该使用拥抱。这是一个仅用于教学目的的玩具。

然而,GHC是用于Haskell的快速​​,多核准备优化编译器。 Get it here。特别是,它进行严格性分析,并编译为本机代码。

关于你的代码突出的主要问题是在一个非常大的列表中使用foldr。可能你想要一个尾递归循环。像这样:

import Data.List

fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

evens x sum | even x    = x + sum
            | otherwise = sum

-- sum of even fibs in first 4M fibs
main = print (foldl' evens 0 (take 4000000 fibs))

除此之外,前4M甚至纤维将占用相当大的空间,因此需要一段时间。

这是the sum of the first 400k even fibs,为您节省一些时间(21秒)。 : - )

答案 1 :(得分:6)

许多观察/提示:

  • 来自偶数的x + sum直到最后才被评估
  • 你正在服用前4,000,000个纤维,而不是纤维直至4,000,000
  • 有一种更简单的方法可以做到这一点

编辑以回复评论

我不会告诉你更简单的方法,因为这是Project Euler问题的乐趣。但我会问你一堆问题:

  • 你可以连续多少个纤维?
  • 如果没有平均纤维,你能走多久?
  • 如果总结所有偶数纤维和所有奇数纤维(手工完成),您对这些总和有何看法?

答案 2 :(得分:4)

你明白这个问题是对的。 actual problem希望你对所有偶数斐波纳契数进行求和,使得斐波那契数本身不超过4百万(恰好只有前33个斐波纳契数)。

答案 3 :(得分:3)

您正在评估fibs的四百万个元素。这些数字呈指数增长。我不知道需要多少字节才能代表百万分之一的Fibonacci数;只有千分之一的斐波那契数字有211个十进制数字,所以这只需要22个32位字来保存数字,不管开头gmp强加什么。而且这些指数呈指数增长。

练习:计算保存四百万斐波纳契数所需的内存量。

答案 4 :(得分:2)

查看Prelude函数takeWhile,filter,even和sum

takeWhile(&lt; 40)[0 ..]
甚至过滤$ takeWhile(&lt; 40)[0 ..]

把他们放在一起:

ans = sum $ filter甚至$ takeWhile(&lt; 4 * 10 ^ 6)fibs