Sieve在开始时陷入困境

时间:2015-07-08 07:34:34

标签: haskell infinite sieve

我写了下面的筛子:

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

2 个答案:

答案 0 :(得分:4)

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

在你的Python版本中,你写了

is_prime(n, ps)

仅对所有到目前为止被筛选的数字进行n测试。你在Haskell中所做的Python相当于

is_prime(n, sieve())

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

答案 1 :(得分:0)

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

或者显然不是我所说的