Zoeken…


Opmerkingen

Stromen worden lui geëvalueerd, wat betekent dat ze kunnen worden gebruikt om generatoren te implementeren, die een nieuw item van het opgegeven type op aanvraag leveren of 'genereren', in plaats van vóór het feit. Dit zorgt ervoor dat alleen de benodigde berekeningen worden uitgevoerd.

Een stream gebruiken om een willekeurige reeks te genereren

genRandom creëert een stroom van willekeurige getallen die een kans van één op vier heeft om te eindigen telkens wanneer het wordt opgeroepen.

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.

Let op het #:: -construct, dat lui terugkeert : omdat het het huidige willekeurige nummer voor een stream plaatst, evalueert het de rest van de stream niet totdat het wordt herhaald.

Oneindige streams via recursie

Stromen kunnen worden gebouwd die naar zichzelf verwijzen en zo oneindig recursief worden.

// 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 deze context is het verschil tussen Var, Val en Def interessant. Als def elk element herberekend telkens wanneer er naar wordt verwezen. Als val elk element wordt vastgehouden en hergebruikt nadat deze is berekend. Dit kan worden aangetoond door bij elke berekening een neveneffect te creëren.

// 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

Dit verklaart ook waarom de random number Stream niet als werkt val .

val rndInt: Stream[Int] = (util.Random.nextInt(90)+10) #:: rndInt
rndInt.take(5)  // (79, 79, 79, 79, 79)

Oneindige zelfreferente stroom

// 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
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow