首页 文章

斯卡拉对期货的“理解”

提问于
浏览
48

我正在阅读斯卡拉食谱(http://shop.oreilly.com/product/0636920026914.do

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

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

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

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

3 回答

  • 112

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

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

    它看起来像一个命令式计算(monad是什么),你将计算结果绑定到 r . 并且 yield 部分被打入 map 电话 . 结果类型取决于 monad 的类型 .

    Future trait具有 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)
    
  • 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]]
    

    使用这些函数调用 within 进行理解 .

    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 函数调用是 not executed ,直到 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.
    

    for comprehensions之外调用这些函数 createAsciiCharscreateIntegers 将是异步执行 .

  • -1

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

相关问题