我正在使用GHCi尝试解决Project Euler上的问题2。
http://projecteuler.net/problem=2
我将无限列表fibs定义为:
前奏> let fibs = 1:2:zipWith(+)fibs(tail fibs)
我尝试以下列方式使用列表推导:
前奏> [x | x< -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 ..],xmod
2 == 0,x <4000000]
但是shell挂起了第二个命令。我很困惑为什么列表推导可以构建列表但sum函数无法处理它。
我发现有一个可行的解决方案
前奏&GT; sum $ filter甚至$ takeWhile(&lt; = 4000000)fibs
但是再次让我感到困惑的是,当列表理解方法没有时,为什么会有效。
答案 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中,我们必须使用takeWhile
或take
来模拟剪辑。
答案 2 :(得分:0)
将[1..]
替换为fibs
,您的第二个变体将与第一个变体相同。