Scala Language
I flussi
Ricerca…
Osservazioni
Gli stream vengono valutati pigramente, nel senso che possono essere utilizzati per implementare i generatori, che forniranno o "genereranno" un nuovo elemento del tipo specificato su richiesta, piuttosto che prima del fatto. Ciò garantisce che vengano eseguiti solo i calcoli necessari.
Utilizzo di un flusso per generare una sequenza casuale
genRandom
crea un flusso di numeri casuali che ha una possibilità su quattro di terminare ogni volta che viene chiamato.
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.
Nota il costrutto #::
, che ricorre pigramente : poiché sta anteponendo il numero casuale corrente a uno stream, non valuta il resto del flusso finché non viene iterato.
Stream infiniti tramite ricorsione
Si possono costruire flussi che si riferiscono a se stessi e quindi diventano infinitamente ricorsivi.
// 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 questo contesto, la differenza tra Var, Val e Def è interessante. Come def
ogni elemento viene ricalcolato ogni volta che viene referenziato. Come valore val
ogni elemento viene mantenuto e riutilizzato dopo che è stato calcolato. Questo può essere dimostrato creando un effetto collaterale con ogni calcolo.
// 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
Questo spiega anche perché il numero casuale Stream
non funziona come val
.
val rndInt: Stream[Int] = (util.Random.nextInt(90)+10) #:: rndInt
rndInt.take(5) // (79, 79, 79, 79, 79)
Stream infinito autoreferenziale
// 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, ?)")