筛子卡在开头

Sieve gets stuck at the beginning

我写了下面的筛子:

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

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

在你的Python版本中,你写了

is_prime(n, ps)

仅测试 n 目前筛选出的所有数字。 Python 相当于你在 Haskell 中所做的基本上是

is_prime(n, sieve())

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

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

或者明显不是我说的