斯卡拉的“理解”与未来

我正在通读 Scala 烹饪书(http://shop.oreilly.com/product/0636920026914.do)

有一个与 Future use 相关的例子涉及到理解。

到目前为止,我对于理解的理解是,当与一个集合一起使用时,它将生成具有相同类型的另一个集合。例如,如果每个 futureX都是 Future[Int]类型,那么下面这些也应该是 Future[Int]类型:

for {
r1 <- future1
r2 <- future2
r3 <- future3
} yield (r1+r2+r3)

有人能解释一下在这段代码中使用 <-时到底发生了什么吗? 我知道如果它是一个生成器,它将通过循环获取每个元素。

53378 次浏览

首先是理解。它被回答了很多次,以至于它是一对一元操作的抽象: mapflatMapwithFilter。当你使用 <-的时候,scalac 把这些线分解成一元的 flatMap:

r <- monadmonad.flatMap(r => ... )

它看起来像一个命令式计算(单子的全部内容) ,您将计算结果绑定到 r。并将 yield部分设计成 map调用。结果类型取决于 monad的类型。

Future性状具有 flatMapmap功能,所以我们可以用它来理解。在您的示例中,可以设计成以下代码:

future1.flatMap(r1 => future2.flatMap(r2 => future3.map(r3 => r1 + r2 + r3) ) )

除了平行论

不用说,如果 future2的执行依赖于 r1,那么您就无法避免顺序执行,但是如果未来的计算是独立的,那么您有两种选择。可以强制执行顺序执行,也可以强制执行 允许并行执行。不能用 执行处理后者,因为执行上下文将处理这个问题。

val res = for {
r1 <- computationReturningFuture1(...)
r2 <- computationReturningFuture2(...)
r3 <- computationReturningFuture3(...)
} yield (r1+r2+r3)

将始终按顺序运行。这可以很容易地用去糖化来解释,之后的 computationReturningFutureX调用仅在 latMaps 内部调用,即。

computationReturningFuture1(...).flatMap(r1 =>
computationReturningFuture2(...).flatMap(r2 =>
computationReturningFuture3(...).map(r3 => r1 + r2 + r3) ) )

然而,这种方法能够并行运行,for 理解能够汇总结果:

val future1 = computationReturningFuture1(...)
val future2 = computationReturningFuture2(...)
val future3 = computationReturningFuture3(...)


val res = for {
r1 <- future1
r2 <- future2
r3 <- future3
} yield (r1+r2+r3)

如果可能的话,它允许 r1r2r3并行运行。这可能是不可能的,取决于有多少线程可用于执行 Future 计算,但是通过使用这种语法,您告诉编译器如果可能的话并行运行这些计算,然后在所有计算完成后执行 yield()

为了详细说明这些现有的答案,这里用一个简单的结果来演示 for理解是如何工作的。

它有点冗长的功能,但它们值得研究一下。

一个给出整数范围的函数

scala> def createIntegers = Future{
println("INT "+ Thread.currentThread().getName+" Begin.")
val returnValue = List.range(1, 256)
println("INT "+ Thread.currentThread().getName+" End.")
returnValue
}
createIntegers: createIntegers: scala.concurrent.Future[List[Int]]

给出一个字符范围的函数

scala> def createAsciiChars = Future{
println("CHAR "+ Thread.currentThread().getName+" Begin.")
val returnValue = new ListBuffer[Char]
for (i <- 1 to 256){
returnValue += i.toChar
}
println("CHAR "+ Thread.currentThread().getName+" End.")
returnValue
}
createAsciiChars: scala.concurrent.Future[scala.collection.mutable.ListBuffer[Char]]

使用这些函数调用 内心作为理解。

scala> val result = for{
i <- createIntegers
s <- createAsciiChars
} yield i.zip(s)
Await.result(result, Duration.Inf)
result: scala.concurrent.Future[List[(Int, Char)]] = Future(<not completed>)

对于以下几行,我们可以看出所有的函数调用都是同步的,也就是说,在 createIntegers完成执行之前,createAsciiChars函数调用是 没有被处决

scala> INT scala-execution-context-global-27 Begin.
INT scala-execution-context-global-27 End.
CHAR scala-execution-context-global-28 Begin.
CHAR scala-execution-context-global-28 End.

使这些函数 createAsciiCharscreateIntegers调用外的 for理解将是异步执行。