Scalac e REPL stanno bene con quel codice (usando val) finché la variabile è un campo di una classe piuttosto che una variabile locale. Puoi rendere la variabile pigra per soddisfare Scala Kata, ma in genere non vorresti usare def in questo modo (ovvero, def un Stream in termini di se stesso) in un vero programma. In tal caso, viene creato un nuovo flusso ogni volta che viene richiamato il metodo, quindi i risultati dei calcoli precedenti (che vengono salvati nello Stream) non possono mai essere riutilizzati. Se si utilizzano molti valori da un tale Stream, le prestazioni saranno terribili e alla fine si esaurirà la memoria.
Questo programma illustra il problema con l'utilizzo DEF in questo modo:
// Show the difference between the use of val and def with Streams.
object StreamTest extends App {
def sum(p:(Int,Int)) = { println("sum " + p); p._1 + p._2 }
val fibs1: Stream[Int] = 0 #:: 1 #:: (fibs1 zip fibs1.tail map sum)
def fibs2: Stream[Int] = 0 #:: 1 #:: (fibs2 zip fibs2.tail map sum)
println("========== VAL ============")
println("----- Take 4:"); fibs1 take 4 foreach println
println("----- Take 5:"); fibs1 take 5 foreach println
println("========== DEF ============")
println("----- Take 4:"); fibs2 take 4 foreach println
println("----- Take 5:"); fibs2 take 5 foreach println
}
Ecco l'output:
========== VAL ============
----- Take 4:
0
1
sum (0,1)
1
sum (1,1)
2
----- Take 5:
0
1
1
2
sum (1,2)
3
========== DEF ============
----- Take 4:
0
1
sum (0,1)
1
sum (0,1)
sum (1,1)
2
----- Take 5:
0
1
sum (0,1)
1
sum (0,1)
sum (1,1)
2
sum (0,1)
sum (0,1)
sum (1,1)
sum (1,2)
3
noti che quando abbiamo usato val:
- Il " prendere 5 "non ha ricalcolato i valori calcolati dal" Take 4 ".
- Il calcolo del 4 ° valore nel "Take 4" non ha comportato la ricalcolo del 3 ° valore.
Ma nessuno di questi è vero quando usiamo def. Ogni utilizzo di Stream, inclusa la propria ricorsione, inizia da zero con un nuovo Stream. Poiché produrre il valore Nth richiede che vengano prima prodotti i valori per N-1 e N-2, ognuno dei quali deve produrre i propri due predecessori e così via, il numero di chiamate a sum() richiesto per produrre un valore cresce molto come la sequenza di Fibonacci stessa: 0, 0, 1, 2, 4, 7, 12, 20, 33, .... E dal momento che tutti questi flussi sono allo stesso tempo, si esaurisce rapidamente la memoria.
Pertanto, date le scarse prestazioni e problemi di memoria, in genere non si desidera utilizzare def durante la creazione di un flusso.
Ma potrebbe darsi che in realtà do desideri un nuovo flusso ogni volta. Supponiamo che tu voglia un flusso di numeri interi casuali, e ogni volta che accedi al flusso desideri nuovi numeri interi, non una riproduzione di interi precedentemente calcolati. E quei valori precedentemente calcolati, dal momento che non si desidera riutilizzarli, occuperebbero inutilmente spazio sull'heap. In questo caso ha senso utilizzare def in modo da ottenere un nuovo flusso di volta in volta e non tenere su di esso, in modo che possa essere garbage collection:
scala> val randInts = Stream.continually(util.Random.nextInt(100))
randInts: scala.collection.immutable.Stream[Int] = Stream(1, ?)
scala> (randInts take 1000).sum
res92: Int = 51535
scala> (randInts take 1000).sum
res93: Int = 51535 <== same answer as before, from saved values
scala> def randInts = Stream.continually(util.Random.nextInt(100))
randInts: scala.collection.immutable.Stream[Int]
scala> (randInts take 1000).sum
res94: Int = 49714
scala> (randInts take 1000).sum
res95: Int = 48442 <== different Stream, so new answer
Fare randInts un metodo ci fa ottenere un nuovo flusso ogni volta, in modo da ottenere nuovi valori e il flusso può essere raccolto.
Si noti che ha senso usare def qui perché i nuovi valori non dipendono dai vecchi valori, quindi randInts non è definito in termini di se stesso.Stream.continually
è un modo semplice per produrre flussi di questo tipo: basta dire come creare un valore e crea un flusso per te.
Sei sicuro che sia una limitazione del compilatore di presentazione e non solo un campo rispetto a cosa variabile locale? –
Buon punto, Luigi. Ho fatto ancora un po 'di sperimentazione dopo aver letto il tuo commento e ora non penso di capire completamente quale sia il problema, ma penso che sia legato al modo in cui quegli strumenti racchiudono il codice. Ottengo l'errore in un oggetto in un foglio di lavoro Scala, ma non in una classe, ed entrambi funzionano con Scalac. Modificheremo la risposta per non incolpare il PC. – AmigoNico
Se lo avvolgi con un oggetto funziona perfettamente: http://www.scalakata.com/50975187e4b093f3524f3685 –