我发现很难理解Haskell将如何评估此primes
函数。是primes
函数得到一遍又一遍的评估,还是primes
函数中的primeFactors
将指向第一个primes
?
primes = 2 : filter ((==1) . length . primeFactors) [3,5..]
primeFactors n = factor n primes
where
factor n (p:ps)
| p * p > n = [n]
| n `mod` p == 0 = p : factor (n `div` p) (p:ps)
| otherwise = factor n ps
main :: IO ()
main = print $ length . take 100000 $ primes
答案 0 :(得分:7)
primes
只是一个列表。它的第一个元素为2,其余元素取自由函数primeFactors
(部分)过滤的奇数整数列表。
primeFactors
使用primes
。这不是通函吗?
不完全是。由于Haskell是惰性的,primeFactors
一次不需要primes
中的所有值,只需小于或等于其参数平方根的值(p:ps
会与primes
,但是如果ps
,我们只需要p*p <= n
,并且这些素数都是在先前对primeFactors
的调用中找到的。
作为示例,跟踪对primeFactors
的前几个调用。为简便起见,让b = (==1) . length . primeFactors
。
primeFactors 3 == factor 3 primes
-- only unpack as much of primes as we need for the next step
== factor 3 (2:filter b [3,5..])
-- because 2*2 > 3, that's only one level
== [3]
因此,由于b [3]
为真,因此我们知道3是primes
的下一个元素。也就是说,primes = 2:3:filter b [5,7..]
primeFactors 5 == factor 5 primes
== factor 5 (2:3:filter b [3,5..])
-- 2*2 > 5 is false, as is 5 `mod` 2 == 0, so
== factor 5 (3:filter b [3,5..])
-- 3*3 > 5, so
== [5]
b [5]
是对的,所以5
是primes
的下一个元素。
primeFactors 7 == factor 7 primes
== factor 7 (2:3:5:filter b [3,5..])
== factor 7 (3:5:filter b [3,5..])
-- 3*3 > 7
== [7]
并且b [7]
是正确的,因此7
是primes
的下一个元素。 (似乎所有内容都已添加到primes
中,不是吗?再调用一次primeFactors
会显示不是这种情况)
primeFactors 9 == factor 9 primes
== factor 9 (2:3:5:7:filter b [3,5..])
-- 2*2 > 9 and 9 `mod` 2 == 0 are false
== factor 9 (3:5:7:filter b [3,5..])
-- 3*3 > 9 is false, but 9 `mod` 3 == 0 is true, so
== 3 : factor (9 `div` 3) (3:5:7:filter b [3,5..])
== 3 : factor 3 (3:5:7:filter b [3,5..])
-- 3*3 > 3 is false, but 3 `mod` 3 == 0, so
== 3 : [3] == [3,3]
但是由于b [3,3]
为假,因此9
不是primes
的元素。所以现在我们有
primes = 2:3:5:7:filter b [3,5..])
要追踪这个过程是一个漫长而乏味的过程,但是您应该感觉到primes
始终处于primeFactors
的“前面”; primes
所需的primeFactors
元素始终由对primeFactors
的早期调用来确定。
答案 1 :(得分:4)
Haskell将如何评估素数函数?
如您所讨论的代码所示,它会打印出前100000个质数,那么primes
的工作方式是什么?
首先,要生成第一个素数,很简单,只需列表的第一个元素即可
2 : filter ((==1) ...
即2
,对于下一个,我们需要将primeFactors
函数应用为
primeFactors 3 = factor 3 primes
现在可能会使对Haskell陌生的人感到困惑,如何评估以上表达式中的primes
?答案是,这只是一个元素为[2,...]
的列表,这要归功于懒惰的求值,现在,我们不需要求值primes
函数生成的列表的所有质数。我们只需要评估下一个,然后看看会发生什么。因此,我们得到2
,上面的表达式变为:
primeFactors 3 = factor 3 [2,..]
和
factor 3 (2:ps) | 2 * 2 > 3 = [3]
因此,primeFactors 3
重新运行[3]
所以
2: filter ((==1) . length . primeFactors) 3 = [2,3]
我们现在成功地生成了2个质数,但是我们需要100000,接下来呢?显然,我们将5
应用于以下表达式:
2: filter ((==1) . length . primeFactors) 5
重复上述步骤:
primeFactors 5 = factor 5 [2,3,..]
这一次我们在列表中有2个元素:
factor 5 [2,3..]
和
factor 5 [2,3..] | otherwise = factor 5 [3,...]
和
factor 5 [3,...] | 3 * 3 > 5 = [5]
重复一次直到生成100000个质数,再一次,由于懒惰的求值,我们不需要100001质数,因此计算停止并打印出结果。