Принятый ответ от @nikie не очень эффективен, он становится относительно медленным после нескольких тысяч, но ответ @sleepynate намного лучше. Мне потребовалось некоторое время, чтобы понять это, поэтому здесь тот же код, но только с переменными, названными более четко:
lazyPrimes :: [Integer]
lazyPrimes = 2: 3: calcNextPrimes (tail lazyPrimes) [5, 7 .. ]
where
calcNextPrimes (p:ps) candidates =
let (smallerSquareP, (_:biggerSquareP)) = span (< p * p) candidates in
smallerSquareP ++ calcNextPrimes ps [c | c <- biggerSquareP, rem c p /= 0]
Основная идея состоит в том, что кандидаты на следующие простые числа уже не содержат чисел, которые делятся на любое простое число, меньшее первого простого числа, данного функции. Так что если вы позвоните
calcNextPrimes (5:ps) [11,13,17..]
список кандидатов не содержит числа, которое делится на 2
или 3
, что означает, что первым непростым кандидатом будет 5 * 5
, потому что 5* 2
и 5 * 3
и 5 * 4
уже ликвидирован. Это позволяет вам взять всех кандидатов, которые меньше квадрата 5, и сразу добавить их к простым числам, а остальное просеять, чтобы исключить все числа, делимые на 5.