I'm trying to find all the primes less than some integer n
as concisely as possible, using list comprehensions. I'm learning Haskell, and this is just an exercise. I'd like to write something like:
isqrt :: Integral a => a -> a
isqrt = floor . sqrt . fromIntegral
primes :: Integral a => a -> [a]
primes n = [i | i <- [1,3..n], mod i k /= 0 | k <- primes (isqrt i)]
which of course doesn't work. Is there a way to have a list comprehension inside a list comprehension?
Here is the error I'm getting:
exercise-99-1.hs:138:39: Not in scope: `k'
exercise-99-1.hs:138:46:
Illegal parallel list comprehension: use -XParallelListComp
exercise-99-1.hs:138:68: Not in scope: `i'
BUT - I wasn't really expecting the syntax to even be legit :-)
The intent was to translate as directly as possible: " primes n
= the set of odd integers i
less than n
such that i
is not divisible by any k
, for all k
in the set: primes (isqrt i)
" - more or less. (I hope I got that right?)
Thanks!
I made some progress with the following:
Is there a shorter way to write the lambda predicate?
edit: Yes, there is, thanks to the remarks in the comments!
your code,
is interpreted as parallel list comprehension; i.e. instead of combining the two generators in the usual nested fashion, they are combined in parallel:
Instead, to express what you intended, it can be written as
thus having a list comprehension inside a list comprehension - but as part of a guard, not generator. The function
and :: [Bool] -> Bool
makes this possible.Incidentally, calling
primes
recursively for each new candidatei
makes it run slow, with run times growing too fast, empirically:Optimal trial division usually runs at ~ n1.4..1.45, list-based sieve of Eratosthenes at ~ n1.2..1.25 and, if optimally implemented on mutable arrays, at ~ n1.0..1.1 (with n the number of primes produced, not the upper limit).