原谅我的愚蠢问题,我是Haskell的新手。
我在Haskell中尝试了以下内容:
sum [fib n| n <- [1..], (even (fib n) && fib n < 4000000)]
需要无限的时间。如果我遗漏n <- [1..]
,解决方案就会立即生效。
我认为这无关紧要,因为Haskell正在评估懒惰。我是否误解了懒惰的评价?
答案 0 :(得分:7)
请注意
sum [ n | n <- [1..], n < 10 ]
也不会终止,因为它会尝试所有可能的n
,以防万一找到另一个元素&#34;小于10&#34;所以它包含在总和中。
相比之下,
sum $ takeWhile (< 10) [ n | n <- [1..] ]
将终止,因为只要发现某个项目不满足谓词takeWhile
,<10
就会丢弃列表的其余部分。
答案 1 :(得分:6)
你的列表理解
sum [fib n | n <- [1..], even (fib n) && fib n < 4000000]
等同于表达式
sum $ map fib $ filter (\n -> even (fib n) && fib n < 4000000) [1..]
查看filter
:
filter :: (a -> Bool) -> [a] -> [a]
filter predicate [] = []
filter predicate (x:xs)
| predicate x = x : filter predicate xs
| otherwise = filter predicate xs
我们可以看到它始终会检查列表中的每个元素,直到它到达列表的末尾。提供给表达式中过滤的列表是[1..]
,这是无限的。这在Haskell中很好,它只是意味着如果强制评估整个列表,过滤器将永远不会完成。然后你把它传递给map fib
,它也可以很好地处理无限列表,但是你得到的是你将它传递给sum
,这需要有一定数量的元素加在一起。
要解决此问题,正如@chi指出的那样,您可以改为使用takeWhile
:
sum $ map fib $ filter (\n -> even (fib n)) $ takeWhile (\n -> fib n < 4000000) [1..]
虽然我会注意到您在此表达式中应用fib
3次不同的时间。什么是最好的是map fib
首先,然后你不必再次申请:
sum $ filter even $ takeWhile (< 4000000) $ map fib [1..]