Scala Language
Streams
Suche…
Bemerkungen
Streams werden faul bewertet, dh sie können zur Implementierung von Generatoren verwendet werden, die auf Abruf ein neues Element des angegebenen Typs bereitstellen oder "erzeugen" und nicht vor der Tatsache. Dadurch wird sichergestellt, dass nur die erforderlichen Berechnungen ausgeführt werden.
Verwenden eines Streams zum Erzeugen einer zufälligen Sequenz
genRandom
erstellt einen Stream von Zufallszahlen, der bei jedem genRandom
eine Chance von eins zu vier hat.
def genRandom: Stream[String] = {
val random = scala.util.Random.nextFloat()
println(s"Random value is: $random")
if (random < 0.25) {
Stream.empty[String]
} else {
("%.3f : A random number" format random) #:: genRandom
}
}
lazy val randos = genRandom // getRandom is lazily evaluated as randos is iterated through
for {
x <- randos
} println(x) // The number of times this prints is effectively randomized.
Beachten Sie das #::
-Konstrukt , das langsam rekonstruiert : Da es die aktuelle Zufallszahl einem Stream voranstellt, wertet es den Rest des Streams nicht aus, bis er durchlaufen wird.
Unendliche Streams über Rekursion
Streams können aufgebaut werden, die sich selbst referenzieren und somit unendlich rekursiv werden.
// factorial
val fact: Stream[BigInt] = 1 #:: fact.zipWithIndex.map{case (p,x)=>p*(x+1)}
fact.take(10) // (1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880)
fact(24) // 620448401733239439360000
// the Fibonacci series
val fib: Stream[BigInt] = 0 #:: fib.scan(1:BigInt)(_+_)
fib.take(10) // (0, 1, 1, 2, 3, 5, 8, 13, 21, 34)
fib(124) // 36726740705505779255899443
// random Ints between 10 and 99 (inclusive)
def rndInt: Stream[Int] = (util.Random.nextInt(90)+10) #:: rndInt
rndInt.take(10) // (20, 95, 14, 44, 42, 78, 85, 24, 99, 85)
In diesem Zusammenhang ist der Unterschied zwischen Var, Val und Def interessant. Als def
jedes Element jedes Mal neu berechnet, wenn es referenziert wird. Als val
jedes Element beibehalten und wiederverwendet, nachdem es berechnet wurde. Dies kann demonstriert werden, indem bei jeder Berechnung ein Nebeneffekt erstellt wird.
// def with extra output per calculation
def fact: Stream[Int] = 1 #:: fact.zipWithIndex.map{case (p,x)=>print("!");p*(x+1)}
fact(5) // !!!!!!!!!!!!!!! 120
fact(4) // !!!!!!!!!! 24
fact(7) // !!!!!!!!!!!!!!!!!!!!!!!!!!!! 5040
// now as val
val fact: Stream[Int] = 1 #:: fact.zipWithIndex.map{case (p,x)=>print("!");p*(x+1)}
fact(5) // !!!!! 120
fact(4) // 24
fact(7) // !! 5040
Dies erklärt auch , warum die Zufallszahl - Stream
nicht als Arbeit val
.
val rndInt: Stream[Int] = (util.Random.nextInt(90)+10) #:: rndInt
rndInt.take(5) // (79, 79, 79, 79, 79)
Unendlich selbstreferenzierender Stream
// Generate stream that references itself in its evaluation lazy val primes: Stream[Int] = 2 #:: Stream.from(3, 2) .filter { i => primes.takeWhile(p => p * p <= i).forall(i % _ != 0) } .takeWhile(_ > 0) // prevent overflowing // Get list of 10 primes assert(primes.take(10).toList == List(2, 3, 5, 7, 11, 13, 17, 19, 23, 29)) // Previously calculated values were memoized, as shown by toString assert(primes.toString == "Stream(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, ?)")