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


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow