2012-09-18 28 views
10

En la siguiente situaciónScala función de procesador del Flujo recursiva de cola definido en rasgo sostiene referencia a la corriente de cabeza

trait T { 

@tailrec 
def consume[A](as: Stream[A]): Unit = { 
    if (as.isEmpty)() 
    else consume(as.tail) 
    } 
} 

object O extends T 

llamando O.consume(Range(1, N).toStream) con N lo suficientemente grande, el programa se ejecutará sin memoria, o al menos consumirá O (N) en lugar de O (1) necesario.

+0

Ver también http://stackoverflow.com/questions/12529697/how-to-write-non-leaking-tail-recursive-stream-function-in-scala – ron

Respuesta

10

El método recursivo de cola se genera para el rasgo. La entrada de método en el extensor del rasgo (aquí O) reenvía la llamada al método del rasgo, pero al hacerlo, guarda una referencia al encabezado del Flujo.

Por lo tanto, el método es recursivo de cola, pero la memoria aún no se puede liberar. Remedio: No defina las funciones de Stream en los rasgos, solo directamente en los objetos.

Una alternativa es scalaz's EphemeralStream, que contiene referencias débiles a la cabecera y cola del flujo, y las recalcula a demanda.

+0

[EphemeralStream] (https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/EphemeralStream.scala) –

2

Hay una solución simple. Sólo envolver su cola consumidor corriente recursiva en otra función que recibe el flujo a través de un parámetro por nombre:

import scala.annotation.tailrec 

trait T { 
    def consume[A](as: => Stream[A]): Unit = { 
    @tailrec 
    def loop[A](as: Stream[A]): Unit = { 
     if (as.isEmpty)() 
     else loop(as.tail) 
    } 
    loop(as) 
    } 
} 

object O extends T { 
    def main(args: Array[String]): Unit = 
    O.consume(Range(1, 1000000000).toStream) 
} 

El método promotor llevará a cabo una referencia a un función calcular una expresión el resultado de que es una corriente :

public final class O$ implements T { 
    public static final MODULE$; 
    // This is the forwarder: 
    public <A> void consume(Function0<Stream<A>> as) { 
    T.class.consume(this, as); 
    } 
    . . .  

    public void main(String[] args) { 
    consume(new AbstractFunction0() { 
     public final Stream<Object> apply() { 
     return package..MODULE$.Range().apply(1, 1000000000).toStream(); 
     } 
    }); 
    } 
} 
Cuestiones relacionadas