Scalac und die REPL sind mit diesem Code (mit val) in Ordnung, solange die Variable ein Feld einer Klasse und nicht eine lokale Variable ist. Sie können die Variable faul machen, um Scala Kata zu befriedigen, aber Sie würden in der Regel nicht def in einem echten Programm auf diese Weise verwenden (d. H. Einen Stream in Bezug auf sich selbst definieren). Wenn Sie dies tun, wird bei jedem Aufruf der Methode ein neuer Stream erstellt, sodass die Ergebnisse früherer Berechnungen (die im Stream gespeichert sind) nie wiederverwendet werden können. Wenn Sie viele Werte aus einem solchen Stream verwenden, ist die Leistung sehr schlecht und Sie haben möglicherweise keinen Speicher mehr.
Dieses Programm demonstriert das Problem mit def auf diese Weise mit:
// 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
}
Hier ist die Ausgabe:
========== 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
Beachten Sie, dass, wenn wir gebraucht val:
- Der " take 5 "hat die von" take 4 "berechneten Werte nicht neu berechnet.
- Die Berechnung des 4. Wertes in "Take 4" hat nicht zur Folge, dass der 3. Wert neu berechnet wird.
Aber keiner von denen ist wahr, wenn wir Def verwenden. Jede Verwendung des Streams, einschließlich seiner eigenen Rekursion, beginnt bei Null mit einem neuen Stream. Da das Erzeugen des N-ten Wertes erfordert, dass wir zuerst die Werte für N-1 und N-2 erzeugen, von denen jedes seine eigenen zwei Vorgänger usw. erzeugen muss, wächst die Anzahl der Aufrufe von sum(), die zur Erzeugung eines Wertes benötigt werden, sehr ähnlich die Fibonacci-Folge selbst: 0, 0, 1, 2, 4, 7, 12, 20, 33, .... Und da alle diese Streams gleichzeitig auf dem Heap sind, ist der Speicher schnell erschöpft.
Aufgrund der schlechten Leistung und des Speicherproblems möchten Sie def normalerweise nicht verwenden, um einen Stream zu erstellen.
Aber es könnte sein, dass Sie eigentlich tun wollen jedes Mal einen neuen Stream. Angenommen, Sie möchten einen Stream mit zufälligen Ganzzahlen, und jedes Mal, wenn Sie auf den Stream zugreifen, möchten Sie neue Ganzzahlen und keine Wiederholung zuvor berechneter Ganzzahlen. Und diese zuvor berechneten Werte würden unnötigerweise Speicherplatz auf dem Heap belegen, da Sie sie nicht wiederverwenden wollen. In diesem Fall ist es sinnvoll def zu verwenden, so dass Sie einen neuen Stream jedes Mal bekommen und halten Sie nicht, um es auf, so dass es Garbage Collection sein kann:
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
machen randInts ein Verfahren uns veranlasst, Erhalte jedes Mal einen neuen Stream, damit wir neue Werte erhalten und der Stream gesammelt werden kann.
Beachten Sie, dass es nur sinnvoll ist, def hier zu verwenden, da neue Werte nicht von alten Werten abhängen, also ist randInts nicht in sich selbst definiert.Stream.continually
ist eine einfache Möglichkeit, solche Streams zu erzeugen: Sie sagen nur, wie man einen Wert erzeugt und es macht einen Stream für Sie.
Sind Sie sicher, dass es sich um eine Einschränkung des Präsentationscompilers handelt und nicht nur um ein Feld vs. lokale Variable? –
Guter Punkt, Luigi. Ich habe nach dem Lesen Ihres Kommentars noch etwas experimentiert und jetzt glaube ich nicht, dass ich das Problem vollständig verstehe, aber ich denke, es hängt damit zusammen, wie diese Tools den Code einbinden. Ich erhalte den Fehler in einem Objekt in einem Scala-Arbeitsblatt, aber nicht in einer Klasse, und beide arbeiten mit scalac. Ich werde die Antwort ändern, um den PC nicht zu beschuldigen. – AmigoNico
Wenn Sie es mit einem Objekt einpacken, funktioniert es gut: http://www.scalakata.com/50975187e4b093f3524f3685 –