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