第12章——惰性求值和并行集合
即时响应性是一项决定任何应用程序成败的关键因素。其他因素,如商业价值、易用性、 可用性、成本以及回弹性,也很重要,但是即时响应性是最重要的—我们人类大约需要 250 ms 来感知任何的移动,超过 5 s 的延迟就变得不可接受了。任何可以降低响应时间的努力都会 产生巨大的影响,能够使客户更加满意,进而赢得他们的信任。
12.1 释放惰性
Parallel/ShortCircuit.scala
def expensiveComputation() = {
println("...assume slow operation...")
false
}
def evaluate(input: Int): Unit = {
println(s"evaluate called with $input")
if (input >= 10 && expensiveComputation())
println("doing work...")
else
println("skipping")
}
evaluate(0)
evaluate(100)
运行结果
evaluate called with 0
skipping
evaluate called with 100
...assume slow operation...
skipping
Parallel/Eager.scala
val perform = expensiveComputation()
if (input >= 10 && perform)
运行结果
evaluate called with 0
...assume slow operation...
skipping
evaluate called with 100
...assume slow operation...
skipping
Parallel/Eager.scala
@volatile lazy val perform = expensiveComputation()
if (input >= 10 && perform)
println("doing work...")
运行结果
evaluate called with 0
skipping
evaluate called with 100
...assume slow operation...
skipping
Parallel/LazyOrder.scala
import scala.io._
def read = StdIn.readInt()
@volatile lazy val first = read
@volatile lazy val second = read
if (Math.random() < 0.5)
second
println(first - second)
运行结果
> scala LazyOrder.scala
1
2
1
> scala LazyOrder.scala
1
2
-1
>
12.2 释放严格集合的惰性
Parallel/StrictCollection.scala
val people = List(
("Mark", 32),
("Bob", 22),
("Jane", 8),
("Jill", 21),
("Nick", 50),
("Nancy", 42),
("Mike", 19),
("Sara", 12),
("Paula", 42),
("John", 21))
def isOlderThan17(person: (String, Int)) = {
println(s"isOlderThan17 called for $person")
val (_, age) = person
age > 17
}
def isNameStartsWithJ(person: (String, Int)) = {
println(s"isNameStartsWithJ called for $person")
val (name, _) = person
name.startsWith("J")
}
println(people.filter { isOlderThan17 }.filter { isNameStartsWithJ }.head)
运行结果
isOlderThan17 called for (Mark,32)
isOlderThan17 called for (Bob,22)
isOlderThan17 called for (Jane,8)
isOlderThan17 called for (Jill,21)
isOlderThan17 called for (Nick,50)
isOlderThan17 called for (Nancy,42)
isOlderThan17 called for (Mike,19)
isOlderThan17 called for (Sara,12)
isOlderThan17 called for (Paula,42)
isOlderThan17 called for (John,21)
isNameStartsWithJ called for (Mark,32)
isNameStartsWithJ called for (Bob,22)
isNameStartsWithJ called for (Jill,21)
isNameStartsWithJ called for (Nick,50)
isNameStartsWithJ called for (Nancy,42)
isNameStartsWithJ called for (Mike,19)
isNameStartsWithJ called for (Paula,42)
isNameStartsWithJ called for (John,21)
(Jill,21)
Parallel/LazyCollection.scala
println(people.view.filter { isOlderThan17 }.filter { isNameStartsWithJ }.head)
运行结果
isOlderThan17 called for (Mark,32)
isNameStartsWithJ called for (Mark,32)
isOlderThan17 called for (Bob,22)
isNameStartsWithJ called for (Bob,22)
isOlderThan17 called for (Jane,8)
isOlderThan17 called for (Jill,21)
isNameStartsWithJ called for (Jill,21)
(Jill,21)
12.3 终极惰性流
Parallel/NumberGenerator.scala
def generate(starting: Int): Stream[Int] = {
starting #:: generate(starting + 1)
}
println(generate(25))
运行结果
Stream(25, ?)
Parallel/NumberGenerator.scala
println(generate(25).take(10).force)
println(generate(25).take(10).toList)
运行结果
Stream(25, 26, 27, 28, 29, 30, 31, 32, 33, 34)
List(25, 26, 27, 28, 29, 30, 31, 32, 33, 34)
Parallel/NumberGenerator.scala
println(generate(25).takeWhile { _ < 40 }.force)
运行结果
Stream(25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39)
Parallel/Primes.scala
def isDivisibleBy(number: Int, divisor: Int) = number % divisor == 0
def isPrime(number: Int) =
number > 1 && !(2 until number).exists { isDivisibleBy(number, _) }
def primes(starting: Int): Stream[Int] = {
println(s"computing for $starting")
if (isPrime(starting))
starting #:: primes(starting + 1)
else
primes(starting + 1)
}
Parallel/Primes.scala
val primesFrom100 = primes(100)
println(primesFrom100.take(3).toList)
println("Let's ask for more...")
println(primesFrom100.take(4).toList)
运行结果
computing for 100
computing for 101
computing for 102
computing for 103
computing for 104
computing for 105
computing for 106
computing for 107
List(101, 103, 107)
Let's ask for more...
computing for 108
computing for 109
List(101, 103, 107, 109)
12.4 并行集合
Parallel/Weather.scala
import scala.io.Source
import scala.xml._
def getWeatherData(city: String) = {
val response = Source.fromURL(
s"https://raw.githubusercontent.com/ReactivePlatform/" +
s"Pragmatic-Scala-StaticResources/master/src/main/resources/" +
s"weathers/$city.xml")
val xmlResponse = XML.loadString(response.mkString)
val cityName = (xmlResponse \\ "city" \ "@name").text
val temperature = (xmlResponse \\ "temperature" \ "@value").text
val condition = (xmlResponse \\ "weather" \ "@value").text
(cityName, temperature, condition)
}
Parallel/Weather.scala
def printWeatherData(weatherData: (String, String, String)): Unit = {
val (cityName, temperature, condition) = weatherData
println(f"$cityName%-15s $temperature%-6s $condition")
}
Parallel/Weather.scala
def timeSample(getData: List[String] => List[(String, String, String)]): Unit = {
val cities = List(
"Houston,us",
"Chicago,us",
"Boston,us",
"Minneapolis,us",
"Oslo,norway",
"Tromso,norway",
"Sydney,australia",
"Berlin,germany",
"London,uk",
"Krakow,poland",
"Rome,italy",
"Stockholm,sweden",
"Bangalore,india",
"Brussels,belgium",
"Reykjavik,iceland")
val start = System.nanoTime
getData(cities).sortBy(_._1).foreach(printWeatherData)
val end = System.nanoTime
println(s"Time taken: ${(end - start) / 1.0e9} sec")
}
Parallel/Weather.scala
timeSample { cities =>
cities.map(getWeatherData)
}
运行结果
Bengaluru 84.2 few clouds
Berlin 45.63 broken clouds
Boston 52.23 scattered clouds
Brussels 50.83 Sky is Clear
Chicago 46.13 sky is clear
Cracow 40.39 moderate rain
Houston 54.01 light intensity drizzle
London 55.33 Sky is Clear
Minneapolis 42.82 sky is clear
Oslo 47.3 Sky is Clear
Reykjavik 31.17 proximity shower rain
Rome 58.42 few clouds
Stockholm 47.28 Sky is Clear
Sydney 68.9 Sky is Clear
Tromso 35.6 proximity shower rain
Time taken: 67.208944087 sec
Parallel/Weather.scala
import scala.collection.parallel.CollectionConverters._
timeSample { cities =>
cities.par.map(getWeatherData).toList
}
运行结果
Bengaluru 84.2 few clouds
Berlin 45.63 broken clouds
Boston 52.23 scattered clouds
Brussels 50.83 Sky is Clear
Chicago 46.13 sky is clear
Cracow 40.39 moderate rain
Houston 54.01 light intensity drizzle
London 55.33 Sky is Clear
Minneapolis 42.82 sky is clear
Oslo 47.3 Sky is Clear
Reykjavik 31.17 proximity shower rain
Rome 58.42 few clouds
Stockholm 47.28 Sky is Clear
Sydney 68.9 Sky is Clear
Tromso 35.6 proximity shower rain
Time taken: 0.171599394 sec
1.0.0