项目Euler 2:Haskell实现混乱

时间:2012-07-09 23:15:51

标签: haskell

我正在使用GHCi尝试解决Project Euler上的问题2。
http://projecteuler.net/problem=2

我将无限列表fibs定义为:

  

前奏> let fibs = 1:2:zipWith(+)fibs(tail fibs)

我尝试以下列方式使用列表推导:

  

前奏&GT; [x | x&lt; -fibs,x mod 2 == 0,x <4000000]   [1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393,196418,317811,514229,832040,1346269 ,2178309,3524578
  前奏&GT; sum $ [x | x&lt; - [1 ..],x mod 2 == 0,x <4000000]

但是shell挂起了第二个命令。我很困惑为什么列表推导可以构建列表但sum函数无法处理它。

我发现有一个可行的解决方案

  

前奏&GT; sum $ filter甚至$ takeWhile(&lt; = 4000000)fibs

但是再次让我感到困惑的是,当列表理解方法没有时,为什么会有效。

3 个答案:

答案 0 :(得分:14)

评估时

[x | x<-fibs, x mod 2 == 0, x<4000000]

你的程序/ Haskell编译器不知道这个列表实际上是有限的。当你调用一个完全使用sum之类的列表的函数时,它只会生成斐波纳契数,测试它们x<4000000(并且每次在某个点之后失败)。

实际上,Haskell编译器在一般情况下不能知道理解表达式是否表示有限列表;问题是undecidable

答案 1 :(得分:1)

它也挂在第一个命令上,不是吗? :)

使用 test 的列表理解等同于filter表达式,而不是takeWhile表达式:

sum $ [x | x <- [1..], x mod 2 == 0, x<4000000] ===

sum $ filter (<4000000) $ filter even $ [1..]

这清楚地描述了非终止计算。

Haskell理解没有Prolog的 cut (即“!”)等价物。在Prolog中,我们能够“从测试中”停止计算:

sum(I,Acc,Res):- I >= 4000000, !, Res = Acc ;
  Acc2 is Acc+I, sum(I+1,Acc2,Res).

但在Haskell中,我们必须使用takeWhiletake来模拟剪辑。

答案 2 :(得分:0)

[1..]替换为fibs,您的第二个变体将与第一个变体相同。

编辑:哦对不起,它不会。但它会“少”错误:)我应该多加注意......