Search code examples
haskellinfinitesieve-algorithm

Sieve gets stuck at the beginning


I wrote the following sieve:

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


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

but I don't understand why it gets stuck after the first value. Running take 10 sieve results in [2, and nothing happens. It probably has something to do with infinite recursion. May the problem be that sieve is growing and at the same time it's used inside isPrime? For that reason I also tried modifying isPrime as follows, but without success:

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

EDIT: Surprisingly, @Jubobs's modification works:

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

I cannot understand why this version of takeWhile works while the other does not. I see that with my previous version I tested many unnecessary divisors, but they were in a finite number nontheless.


The code should basically be equivalent to the following Python code:

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

Solution

  • You cause an infinite recursion by applying all to the entire sieve and not just the values which you sieved so far. I.e. For the second element, isPrime tests all values in sieve instead of just 2.

    In your Python version, you wrote

    is_prime(n, ps)
    

    which only tests n against all numbers sieved so far. The Python equivalent of what you did in Haskell is basically

    is_prime(n, sieve())
    

    Now, using takeWhile (<n) won't help because that also requires calculating the sieve elements. Imagine what happens for the second element of sieve (which should be 3): it tests all values of the sieve for which < 3 holds, but in order to test that you actually need to evaluate the sieve elements. So you still have an infinite recursion.