Scala, Erastothenes: есть ли простой способ заменить поток итерацией?
Я написал функцию, которая генерирует простые числа бесконечно (википедия:инкрементальное сито эрастотена) использование потоков. Он возвращает поток, но он также объединяет потоки простых кратных внутри, чтобы пометить предстоящие композиты. Определение является кратким, функциональным, элегантным и простым для понимания, если я так скажу:
def primes(): Stream[Int] = {
def merge(a: Stream[Int], b: Stream[Int]): Stream[Int] = {
def next = a.head min b.head
Stream.cons(next, merge(if (a.head == next) a.tail else a,
if (b.head == next) b.tail else b))
}
def test(n: Int, compositeStream: Stream[Int]): Stream[Int] = {
if (n == compositeStream.head) test(n+1, compositeStream.tail)
else Stream.cons(n, test(n+1, merge(compositeStream, Stream.from(n*n, n))))
}
test(2, Stream.from(4, 2))
}
Но я получаюjava.lang.OutOfMemoryError: Превышен лимит накладных расходов GC " когда я пытаюсь сгенерировать 1000-е простое число.
У меня есть альтернативное решение, которое возвращает итератор над простыми числами и использует внутреннюю очередь приоритетов кортежей (множественное число, простое число, используемое для генерации множественного числа), чтобы пометить предстоящие композиции. Он работает хорошо, но занимает примерно вдвое больше кода, и мне пришлось перезапустить его с нуля:
import scala.collection.mutable.PriorityQueue
def primes(): Iterator[Int] = {
// Tuple (composite, prime) is used to generate a primes multiples
object CompositeGeneratorOrdering extends Ordering[(Long, Int)] {
def compare(a: (Long, Int), b: (Long, Int)) = b._1 compare a._1
}
var n = 2;
val composites = PriorityQueue(((n*n).toLong, n))(CompositeGeneratorOrdering)
def advance = {
while (n == composites.head._1) { // n is composite
while (n == composites.head._1) { // duplicate composites
val (multiple, prime) = composites.dequeue
composites.enqueue((multiple + prime, prime))
}
n += 1
}
assert(n < composites.head._1)
val prime = n
n += 1
composites.enqueue((prime.toLong * prime.toLong, prime))
prime
}
Iterator.continually(advance)
}
Есть ли простой способ перевести код с потоками в код с итераторами? Или есть простой способ сделать мою первую попытку более эффективной памяти?
Это'проще мыслить в терминах потоков; Я'Я предпочел бы начать таким образом, а затем настроить мой код, если это необходимо.