2012-03-25 9 views
10

Normalmente, se si crea un oggetto Stream, la testa sarà valutata con entusiasmo:Quando viene valutata la testa di uno stream?

scala> Stream({println("evaluating 1"); 1} , 2, 3) 
evaluating 1 
res63: scala.collection.immutable.Stream[Int] = Stream(1, ?) 

Se creiamo un flusso a cui anteporre nella stessa istruzione, sembra un po 'sorprendente che la testa non viene valutato prima di la concatenazione. vale a dire

scala> 0 #:: Stream({println("evaluating 1"); 1} , 2, 3) 
res65: scala.collection.immutable.Stream[Int] = Stream(0, ?) 

(#:: è giusto-associativa ed è il metodo prepend su ConsWrapper, che è una classe implicita di Stream.)

Come si fa a non valutare la testa prima di anteponendo il 0? È che la coda Stream (o contro cella) non esiste nell'heap fino a quando non prendiamo i valori dal flusso risultante? Ma se sì, come chiamiamo il metodo #:: su un oggetto che non esiste ancora?

+1

Suggerisco di usare 'javap' per capire cosa sta succedendo. –

+0

L'ho capito guardando la fonte (supponendo che la mia risposta sia corretta) –

risposta

7

-Xprint:typer è un tuo amico, ogni volta che vuoi capire esattamente come viene valutato il codice o se ne deducono i tipi.

scala -Xprint:typer -e '0 #:: Stream({println("evaluating 1"); 1} , 2, 3)' 

val x$1: Int = 0; 
Stream.consWrapper[Int](Stream.apply[Int]({ 
    println("evaluating 1"); 
    1 
}, 2, 3)).#::(x$1) 

Il parametro di consWrapper è il nome. Quindi anche questo funziona:

scala> (1 #:: (sys.error("!!"): Stream[Int])).head 
res1: Int = 1 
+0

+1 per usare '-e'. Penso che sia così poco usato rispetto, per esempio, a Perl. –

+0

Lo capisco adesso. Altrettanto importante è che l'implicito def da Stream a ConsWrapper è per nome. 'implicit def consWrapper [A] (stream: => Stream [A])'. Questo è presumibilmente il motivo per cui esiste "ConsWrapper", e "# ::" non è un metodo direttamente su "Stream": così che il flusso non deve essere creato per chiamare questo metodo - si ottiene solo un oggetto 'ConsWrapper' che contiene il sottoinsieme di funzionalità. –

5

La testa viene valutata nel momento in cui viene creato lo Stream.

Ma nel secondo esempio non si passa uno Streem come secondo argomento a #:: si passa un parametro per nome, vale a dire che l'espressione completa Stream({println("evaluating 1"); 1} , 2, 3) non viene valutata affatto.

Problemi correlati