为了帮助我学习Haskell,我正在解决Project Euler上的问题。在解决了每个问题之后,我会针对Haskell wiki检查我的解决方案,以尝试学习更好的编码实践。以下是the solution至problem 3:
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
problem_3 = last (primeFactors 317584931803)
我天真的解读是,primes
是根据primeFactors
来定义的,primes
是根据primeFactors 9
定义的。因此评估factor 9 primes
将遵循此过程:
primes
。primes
第一个元素,即2。primeFactors 3
下一个元素。primes
。primes
第一个元素,即2。primeFactors 3
下一个元素。换句话说,步骤2-4将无限重复。显然我错了,因为算法终止了。我在这里犯了什么错误?
答案 0 :(得分:17)
primeFactors
只能读取它正在评估的数字的平方根。它永远不会在列表中进一步查看,这意味着它永远不会“赶上”它正在测试包含在列表中的数字。因为Haskell是惰性的,这意味着primeFactors
测试会终止。
要记住的另一件事是primes
不是每次访问时都评估为列表的函数,而是一个懒惰构造的列表。因此,一旦访问了第15个元素,第二次访问它是“免费的”(例如,它不需要任何进一步的计算)。
答案 1 :(得分:8)
凯文的答案令人满意,但请允许我指出你逻辑中的缺陷。这是#6错了。所以我们正在评估primeFactors 3
:
primeFactors 3 ==>
factor 3 primes ==>
factor 3 (2 : THUNK) ==>
2*2 > 3 == True ==>
[3]
永远不需要评估THUNK以确定primeFactor 3
是[3]
。
答案 2 :(得分:7)
primeFactors 3
不会询问primes
下一个元素,只会询问第一个元素,因为2*2
已大于3