斯卡拉对期货的“理解”

时间:2013-09-27 08:14:44

标签: scala future

我正在阅读Scala Cookbook(http://shop.oreilly.com/product/0636920026914.do

有一个与未来使用有关的例子涉及到理解。

到目前为止,我对理解的理解是,当与集合一起使用时,它将产生具有相同类型的另一个集合。例如,如果每个futureX的类型为Future[Int],则以下内容也应为Future[Int]类型:

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

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

3 个答案:

答案 0 :(得分:123)

首先要理解。它在很多时候得到了回答,它是对几个monadic操作的抽象:mapflatMapwithFilter。当您使用<-时,scalac将这些行去掉为monadic flatMap

r <- monad加入monad.flatMap(r => ... )

它看起来像一个命令式计算(monad是什么),你将计算结果绑定到ryield部分被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)

将始终按顺序运行。它可以通过desugaring轻松解释,之后后续的computationReturningFutureX调用仅在flatMaps中调用,即

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

然而,这可以并行运行,并且for comprehension聚合结果:

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

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

答案 1 :(得分:0)

在这里详细说明现有答案,这是一个简单的结果,用于说明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>)

对于以下这些行,我们可以看出所有函数调用都是同步的,即createAsciiChars函数调用未执行,直到createIntegers完成执行。

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.

createAsciiChars理解之外进行这些函数createIntegersfor调用将是异步执行。

答案 2 :(得分:-1)

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