Вот альтернативный способ генерации простых чисел с использованием сита.
Это не особенно эффективно, но демонстрирует ленивые потоки.
Поток похож на список, кроме Хвост представлен функцией, которая не была оценена. Конкретное определение потока ниже не имеет способа представить «конец потока», который обязательно должны иметь строгие списки. Но так как простые числа бесконечны, поток, который их содержит, также может быть концептуально.
datatype 'a stream = Cons of 'a * (unit -> 'a stream)
Простые числа являются подмножеством натуральных чисел,
fun nats i =
Cons (i, fn () => nats (i+1))
fun take 0 _ = []
| take 1 (Cons (x, _)) = [x]
| take n (Cons (x, stream)) = x :: take (n-1) (stream ())
- take 10 (nats 0);
> val it = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] : int list
Что касается выбора простые числа с использованием сита, см. анимацию Сито Эратосфена (Википедия): Мы выбираем первый элемент (начиная с 2) и вычеркиваем последующие кратные (4, 6, 8, ...). Мы выбираем следующий элемент (будучи 3) и вычеркиваем последующие кратные (6, 9, 12, ...). Мы выбираем следующий элемент (5, так как 4 вычеркнуто) и вычеркиваем последующие кратные (10, 15, 20, ...) и т. Д.
Рекурсивное решение с использованием ленивых потоков может выглядеть как
fun sieve (Cons (n, stream)) =
Cons (n, fn () => sieve (remove_multiples n (stream ())))
and remove_multiples i =
filter (fn n => n mod i <> 0)
and filter p (Cons (x, stream)) =
if p x
then Cons (x, fn () => filter p (stream ()))
else filter p (stream ())
Здесь sieve
- рекурсивное, ленивое определение, потому что оно вызывает себя внутри fn () => ...
, что задерживает вычисления. Он постепенно отфильтровывает больше кратных, потому что он не просто вызывает себя с потоком, у которого удален один элемент, но и с потоком, у которого множество элементов удаляется при каждом рекурсивном вызове.
Вы можете взять первые 10 простые числа:
fun primes n =
take n (sieve (nats 2))
- primes 10;
> val it = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] : int list
Или вы можете взять первые простые числа, для которых имеет место предикат:
fun takeWhile p (Cons (x, stream)) =
if p x then x :: takeWhile p (stream ()) else []
fun primesUpto n =
takeWhile (fn p => p <= n) (sieve (nats 2))
- primesUpto 100;
> val it =
[2, 3, 5, 7, 11,
13, 17, 19, 23,
29, 31, 37, 41,
43, 47, 53, 59,
61, 67, 71, 73,
79, 83, 89, 97] : int list
Если вы хотите более эффективную, но все еще функциональную и ленивую стратегию, имейте посмотрите на колесное сито .