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, ?)")


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow