2

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
rubik
  • 8,814
  • 9
  • 58
  • 88
  • 1
    I haven't worked out the details, but you most likely get stuck in a nonterminating computation because you pass `sieve` (instead of only an appropriate prefix of `sieve`, i.e. `takeWhile (\p -> p^2 <= n) sieve`) to `isPrime`. – jub0bs Jul 08 '15 at 08:11
  • @Jubobs I tried adding your suggested modification and it works! Now I am really confused. Why should it work with `takeWhile (\p -> p^2 <= n)` and not with `takeWhile ( – rubik Jul 08 '15 at 08:14

2 Answers2

4

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.

Frerich Raabe
  • 90,689
  • 19
  • 115
  • 207
  • But why doesn't it work when I add the `takeWhile`? With that it should stop after a finite number of values. – rubik Jul 08 '15 at 08:12
  • I'm puzzled. I tried @Jubobs's suggested modification and it works. I cannot understand why his `takeWhile` works while mine does not. – rubik Jul 08 '15 at 08:15
  • Oh now it's clear, thanks! Using the other `takeWhile` works because it only uses already computed primes. – rubik Jul 08 '15 at 08:17
0

I'd say the comprehension list in sieve won't end until it completes, never. To use take like that, sieve would have to return the elements one by one. For instance, [1..] shows 1, 2 ,3 ,4... to infinity but your sieve only shows [2.

Or clearly not what I said

mjgalindo
  • 856
  • 11
  • 26