在Haskell Wiki article on prime numbers中,描述了Eratosthenes筛网的以下实现:
primes = 2 : 3 : minus [5,7..] (unionAll [[p*p, p*p+2*p..] | p <- tail primes])
在做...
primes !! 2
... Haskell如何在这种特定情况下认识到不要尝试p
(又名primes
)尾部的所有[3..]
,而是只减去一组与3
?
换句话说:Haskell如何知道其他p
的任何一个(或其倍数)将不匹配5
,这是最终的答案。有什么好的经验法则可以知道编译器何时足够聪明,可以处理无限情况?</ p>
答案 0 :(得分:4)
(!!)
仅要求对primes
进行足够的评估才能发现至少有3个元素,第三个元素是什么。要获得第三个元素,我们需要开始评估对minus
的调用。
minus
假定其两个参数均已排序。这在文档中进行了描述,并且在primes
的定义中得到了满足。 minus
执行的第一个比较在5到9之间,这表明5是结果的第一个元素。在definition of minus中就是LT -> x : loop xs (y:ys)
。
(!!)
现在具有primes
的第三个元素,因此评估不会在primes
或minus
或unionAll
中继续进行。子表达式的评估与外部表达式中的模式匹配之间的这种来回交互是懒惰的评估。
答案 1 :(得分:2)
实际上,关键在于unionAll
的实现。 minus
只是不知不觉地从其正确的参数中拉出了元素(它假定两个参数当然都没有减少)。
首先,让我们将其重写为
primes = 2 : ps
ps = 3 : t
t = minus [5,7..] (unionAll [[p*p, p*p+2*p..] | p <- ps])
-- primes !! 2 == ps !! 1 == head t
= minus [5,7..] (unionAll [[p*p, p*p+2*p..] | p <- (3 : t)])
= minus [5,7..] (unionAll ([9, 15..] : [[p*p, p*p+2*p..] | p <- t]))
现在unionAll
很聪明:它基于一个假设(在这里是事实),即在unionAll xs
中它认为(map head xs)
处于非递减状态。
如此,它知道它不必比较 9
与任何东西!因此,它只是无条件生成它(您可以查阅其定义以自行检查):
= minus [5,7..]
(9 : union [15, 21..] (unionAll ........))
因此minus
可以将5
和7
与之进行比较,并产生:
= 5 : 7 : minus [9,11..]
(9 : union [15, 21..] (unionAll ........))
所有这些都是因为只知道第一个奇数素数3
。