Scala Language
Corrientes
Buscar..
Observaciones
Los flujos se evalúan perezosamente, lo que significa que se pueden usar para implementar generadores, que proporcionarán o 'generarán' un nuevo elemento del tipo especificado a pedido, en lugar de antes del hecho. Esto asegura que solo se realicen los cálculos necesarios.
Uso de un flujo para generar una secuencia aleatoria
genRandom
crea una secuencia de números aleatorios que tiene una probabilidad de uno en cuatro de terminar cada vez que se llama.
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.
Tenga en cuenta la construcción #::
, que recurre perezosamente : debido a que está anteponiendo el número aleatorio actual a una secuencia, no evalúa el resto de la secuencia hasta que se itera.
Corrientes infinitas a través de la recursión
Se pueden construir flujos que se refieran a sí mismos y, por lo tanto, se vuelvan infinitamente recursivos.
// 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)
En este contexto, la diferencia entre Var, Val y Def es interesante. Como def
cada elemento se recalcula cada vez que se hace referencia. Como valor val
cada elemento se conserva y se reutiliza una vez calculado. Esto se puede demostrar creando un efecto secundario con cada cálculo.
// 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
Esto también explica por qué la Stream
números aleatorios no funciona como un val
.
val rndInt: Stream[Int] = (util.Random.nextInt(90)+10) #:: rndInt
rndInt.take(5) // (79, 79, 79, 79, 79)
Secuencia infinita auto-referente
// 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, ?)")