2011-10-28 17 views
10

? El siguiente código imprime "* 1". Lo que desconcertante es si quito el comentario devuelve "* 4", que es lo que estaba esperando¿Por qué mi takeWhile no funciona con mi Stream

var max = 0 
lazy val list: Stream[Int] = 1 #:: Stream.from(2) 
list.takeWhile { 
    x => 
    max = x 
    x < 4 
}//.foreach(println) 
println("*" + max) 

Respuesta

20

En primer lugar: la lazy en su segunda línea no está haciendo nada, puede eliminarlo y obtener el mismo resultado.

que es más importante: takeWhile es en realidad perezoso, ya que sólo devuelve otra Stream, y nada más allá de la cabeza de esa corriente será evaluado hasta que sea necesario. Considere lo siguiente:

val s = Stream.from(1).takeWhile(_ > 0) 

Usted y yo sabemos que s va a haber un flujo infinito, pero si nos estimulan al REPL y tipo esto en, es perfectamente feliz evaluarlo:

scala> val s = Stream.from(1).takeWhile(_ > 0) 
s: scala.collection.immutable.Stream[Int] = Stream(1, ?) 

Lo mismo está sucediendo en su ejemplo: el (Int) ⇒ Boolean que ha pasado al takeWhile no va a alimentar ningún elemento más allá de la cabecera de la secuencia, hasta que algo como su foreach lo haga necesario.

Esto se puede ver aún más drásticamente mediante la adición de algo así como un println interior de la takeWhile predicado:

scala> val s = Stream.from(1).takeWhile { x => println("Checking: " + x); x < 4 } 
Checking: 1 
s: scala.collection.immutable.Stream[Int] = Stream(1, ?) 

scala> val l = s.toList 
Checking: 2 
Checking: 3 
Checking: 4 
l: List[Int] = List(1, 2, 3) 

Es evidente que el predicado sólo se obtiene la llamada para la cabeza de la corriente, hasta que forzar la evaluación de la resto de la secuencia llamando al toList.

Cuestiones relacionadas