Scala Language
Streams
Поиск…
замечания
Потоки лениво оцениваются, то есть они могут использоваться для реализации генераторов, которые будут предоставлять или «генерировать» новый элемент указанного типа по запросу, а не до этого. Это гарантирует выполнение только необходимых вычислений.
Использование потока для генерации случайной последовательности
genRandom
создает поток случайных чисел, который имеет один из четырех шансов прекратить каждый раз, когда он вызывается.
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.
Обратите внимание на конструкцию #::
, которая лениво рекурсирует : поскольку она добавляет текущее случайное число к потоку, оно не оценивает оставшуюся часть потока до тех пор, пока оно не будет выполнено.
Бесконечные потоки через рекурсию
Потоки могут быть построены так, чтобы ссылаться на них и, таким образом, становиться бесконечно рекурсивными.
// 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)
В этом контексте интересна разница между Var, Val и Def . Как def
каждый элемент пересчитывается каждый раз, когда он ссылается. В качестве значения val
каждый элемент сохраняется и повторно используется после его вычисления. Это можно продемонстрировать, создавая побочный эффект при каждом вычислении.
// 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
Это также объясняет, почему случайное число Stream
не работает как val
.
val rndInt: Stream[Int] = (util.Random.nextInt(90)+10) #:: rndInt
rndInt.take(5) // (79, 79, 79, 79, 79)
Бесконечный поток саморефермента
// 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, ?)")