【问题标题】:Sieve gets stuck at the beginning筛子一开始就卡住了
【发布时间】:2015-09-25 23:40:15
【问题描述】:

我写了以下筛子:

isPrime :: Integer -> [Integer] -> Bool
isPrime n = all (\i -> n `mod` i /= 0)


sieve :: [Integer]
sieve = 2 : [i | i <- [3,5..], isPrime i sieve]

但我不明白为什么它在第一个值之后会卡住。运行take 10 sieve 导致[2, 并且没有任何反应。它可能与无限递归有关。问题可能是sieve 正在增长,同时它在isPrime 内部使用?出于这个原因,我也尝试如下修改isPrime,但没有成功:

isPrime :: Integer -> [Integer] -> Bool
isPrime n = all (\i -> n `mod` i /= 0) . takeWhile (<n)

编辑:令人惊讶的是,@Jubobs 的修改有效:

isPrime :: Integer -> [Integer] -> Bool
isPrime n = all (\i -> n `mod` i /= 0) . takeWhile (\p -> p^2 <= n)

我不明白为什么这个版本的takeWhile 可以工作,而另一个不能。我看到在我之前的版本中,我测试了许多不必要的除数,但它们的数量仍然有限。


代码应该基本上等价于下面的Python代码:

def is_prime(n, ps):
    for i in ps:
        if n % i == 0: return False
    return True


def sieve():
    yield 2
    n = 3
    ps = [2]
    while True:
        if is_prime(n, ps):
            yield n
            ps.append(n)
        n += 2

【问题讨论】:

  • 我还没有弄清楚细节,但你很可能会因为你传递sieve而陷入非终止计算(而不是只有sieve的适当前缀,即takeWhile (\p -&gt; p^2 &lt;= n) sieve)到isPrime
  • @Jubobs 我尝试添加您建议的修改并且它有效!现在我真的很困惑。为什么它应该与takeWhile (\p -&gt; p^2 &lt;= n) 一起工作而不是takeWhile (&lt;n)

标签: haskell infinite sieve


【解决方案1】:

通过将all 应用于整个sieve 而不仅仅是您目前筛选的值,您会导致无限递归。 IE。对于第二个元素,isPrime 测试sieve 中的所有 值,而不仅仅是2

在你的 Python 版本中,你写了

is_prime(n, ps)

仅针对到目前为止筛选的所有数字测试n。你在 Haskell 中所做的 Python 等价物基本上是

is_prime(n, sieve())

现在,使用takeWhile (&lt;n) 将无济于事,因为这还需要计算筛元素。想象一下 sieve 的第二个元素(应该是 3)会发生什么:它测试了 &lt; 3 持有的筛子的所有值,但是为了测试您实际上需要评估筛子元素。所以你仍然有一个无限递归。

【讨论】:

  • 但是为什么我添加takeWhile后它不起作用?这样它应该在有限数量的值之后停止。
  • 我很困惑。我尝试了@Jubobs 建议的修改并且它有效。我不明白为什么他的takeWhile 有效,而我的无效。
  • 哦,现在很清楚了,谢谢!使用另一个 takeWhile 是可行的,因为它只使用已经计算好的素数。
【解决方案2】:

我会说 sieve 中的理解列表在完成之前不会结束,永远不会。要像这样使用 take , sieve 必须一个一个地返回元素。例如,[1..]1, 2 ,3 ,4... 显示为无穷大,但您的筛子仅显示[2

或者显然不是我说的

【讨论】:

    猜你喜欢
    • 2012-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多