Scala Language
Strumienie
Szukaj…
Uwagi
Strumienie są leniwie oceniane, co oznacza, że można ich użyć do wdrożenia generatorów, które dostarczą lub „wygenerują” nowy element określonego typu na żądanie, a nie przed faktem. Zapewnia to wykonanie tylko niezbędnych obliczeń.
Używanie strumienia do generowania losowej sekwencji
genRandom
tworzy strumień liczb losowych, który ma szansę na zakończenie jednej na cztery przy każdym wywołaniu.
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.
Zwróć uwagę na konstrukcję #::
, która leniwie się powtarza : ponieważ przygotowuje bieżącą liczbę losową do strumienia, nie ocenia pozostałej części strumienia, dopóki nie zostanie iterowana.
Nieskończone strumienie poprzez rekurencję
Można budować strumienie, które odwołują się do siebie, dzięki czemu stają się nieskończenie rekurencyjne.
// 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)
W tym kontekście interesująca jest różnica między Var, Val i Def . Z def
każdy element jest ponownie obliczany za każdym razem, gdy się do niego odwołuje. Jako val
każdy element jest zatrzymywane i ponownie wykorzystane po to zostało obliczone. Można to wykazać, tworząc efekt uboczny przy każdym obliczeniu.
// 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
To również wyjaśnia, dlaczego liczba losowa Stream
nie działa jako val
.
val rndInt: Stream[Int] = (util.Random.nextInt(90)+10) #:: rndInt
rndInt.take(5) // (79, 79, 79, 79, 79)
Nieskończony strumień samowystarczalny
// 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, ?)")