【问题标题】:Why is this Haskell code snippet not infinitely recursive?为什么这个 Haskell 代码片段不是无限递归的?
【发布时间】:2012-01-18 03:58:43
【问题描述】:

为了帮助我学习 Haskell,我正在解决 Project Euler 中的问题。解决每个问题后,我会对照 Haskell wiki 检查我的解决方案,以尝试学习更好的编码实践。这里是the solutionproblem 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 定义的,而primeFactors 是根据primes 定义的。所以评估primeFactors 9 将遵循这个过程:

  1. 评估factor 9 primes
  2. primes 询问其第一个元素,即 2。
  3. primes 询问下一个元素。
  4. 作为此过程的一部分,请评估 primeFactors 3
  5. primes 询问其第一个元素,即 2。
  6. primes 询问下一个元素。
  7. 作为此过程的一部分,请评估 primeFactors 3
  8. ...

换句话说,步骤 2-4 将无限重复。显然我错了,因为算法终止了。我在这里犯了什么错误?

【问题讨论】:

  • 因为,正如这里的答案所说,primeFactors 仅访问 primes 直到素数的平方超过被测试的数字,该代码等效于 primes = 2:[n | n<-[3..], all ((> 0).rem n) $ takeWhile ((<= n).(^2)) primes],这显然是非循环的.

标签: haskell primes factorization


【解决方案1】:

primeFactors 只读取它正在评估的数字的平方根。它永远不会在列表中进一步查找,这意味着它永远不会“赶上”它正在测试以包含在列表中的数字。因为 Haskell 是惰性的,这意味着 primeFactors 测试确实终止了。

要记住的另一件事是primes 不是一个每次访问它时都会计算为列表的函数,而是一个延迟构造的列表。因此,一旦第 15 个元素被访问过一次,第二次访问它是“免费的”(例如,它不需要任何进一步的计算)。

【讨论】:

  • 详细信息:第二次访问它仍然需要 15 次取消引用,因为我们正在执行 cons 单元格列表...如果您有数百个列表元素,这可能会很多
  • @sparkleshy 这将是(大约)sqrt(n) 的二次方,即将(大约)线性成本添加到 above-linear 计算中。
【解决方案2】:

primeFactors 3 不向 primes 询问其下一个元素,只询问第一个元素,因为 2*2 已经大于 3

【讨论】:

    【解决方案3】:

    Kevin 的回答令人满意,但请允许我指出您逻辑中的缺陷。 #6 是错误的。所以我们正在评估primeFactors 3

    primeFactors 3          ==>
    factor 3 primes         ==>
    factor 3 (2 : THUNK)    ==>
    2*2 > 3 == True         ==>
    [3]
    

    永远不需要评估 THUNK 来确定 primeFactor 3[3]

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-08-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-07
      • 2013-07-25
      • 1970-01-01
      • 2011-05-24
      相关资源
      最近更新 更多