Scala, Erastothenes: ¿Existe una forma sencilla de reemplazar una secuencia con una iteración?

Escribí una función que genera primos de forma indefinida (wikipedia:Tamiz incremental de Erastothenes) usos corrientes. Devuelve un flujo, pero también combina flujos de múltiplos primos internamente para marcar los próximos compuestos. La definición es concisa, funcional, elegante y fácil de entender, si lo digo yo mismo:

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))
}

Pero, obtengo un "java.lang.OutOfMemoryError: Se excedió el límite de sobrecarga del GC" cuando trato de generar el número 1000 primo.

Tengo una solución alternativa que devuelve un iterador sobre primos y utiliza una cola de prioridad de tuplas (múltiples, primos que se usan para generar múltiples) internamente para marcar los próximos compuestos. Funciona bien, pero toma aproximadamente el doble de código, y básicamente tuve que reiniciar desde cero:

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)
}

¿Existe una forma sencilla de traducir el código con flujos a código con iteradores? ¿O hay una forma sencilla de hacer que mi primer intento sea más eficiente en la memoria?

Es más fácil pensar en términos de corrientes; Prefiero empezar de esa manera, luego ajustar mi código si es necesario.

Respuestas a la pregunta(4)

Su respuesta a la pregunta