74

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

有一个与未来使用相关的示例,涉及理解。

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

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

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

4

3 回答 3

160

首先关于理解。它被多次回答,它是对几个单子操作的抽象:map, flatMap, withFilter. 当您使用时<-,scalac 将这行脱糖成 monadic flatMap

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

它看起来像一个命令式计算(monad 的全部内容),你将计算结果绑定到r. yield部分被脱糖map调用。结果类型取决于monad's 的类型。

Futuretrait 有一个flatMapandmap函数,所以我们可以用它来理解。在您的示例中,可以将其脱糖为以下代码:

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调用仅在 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)
于 2013-09-27T08:25:50.943 回答
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]]

for 理解中使用这些函数调用。

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,理解createIntegers之外的调用将是异步执行。for

于 2018-02-21T11:33:16.250 回答
-1

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

于 2017-04-04T20:48:49.277 回答