Scala for-comprehension从多个变量绑定中产生结果

时间:2018-02-09 07:37:10

标签: scala for-comprehension

我非常好奇斯卡拉是如何贬低以下理解的:

for {
  a <- Option(5)
  b = a * 2
  c <- if (b == 10) Option(100) else None
} yield b + c

我的困难来自于产量中bc,因为它们似乎在不同的步骤中受到约束

3 个答案:

答案 0 :(得分:4)

这是desugar的已清理输出 - Ammonite REPL中提供的命令:

Option(5)
  .map { a =>
    val b = a * 2;
    (a, b)
  }
  .flatMap { case (a, b) =>
      (if (b == 10) Option(100) else None)
        .map(c => b + c)
  }

bc都可以出现在yield中,因为它不会对链式调用map / flatMap,而是嵌套调用。

答案 1 :(得分:1)

这两个代码是等效的:

scala> for {
     |   a <- Option(5)
     |   b = a * 2
     |   c <- if (b == 10) Option(100) else None
     | } yield b + c
res70: Option[Int] = Some(110)

scala> for {
     |   a <- Option(5)
     |   b = a * 2
     |   if (b == 10) 
     |   c <- Option(100) 
     | } yield b + c
res71: Option[Int] = Some(110)

由于不涉及任何集合,产生多个值,因此只有一个重要的步骤 - 或者说,可以说是3到4个小步骤。如果a为None,则整个循环将提前终止,产生None。

desugaring是一个flatMap / withFilter / map。

答案 2 :(得分:1)

您甚至可以询问编译器。以下命令:

scala -Xprint:parser -e "for {
  a <- Option(5)
  b = a * 2
  c <- if (b == 10) Option(100) else None
} yield b + c"

产生此输出

[[syntax trees at end of                    parser]] // scalacmd7617799112170074915.scala
package <empty> {
  object Main extends scala.AnyRef {
    def <init>() = {
      super.<init>();
      ()
    };
    def main(args: Array[String]): scala.Unit = {
      final class $anon extends scala.AnyRef {
        def <init>() = {
          super.<init>();
          ()
        };
        Option(5).map(((a) => {
  val b = a.$times(2);
  scala.Tuple2(a, b)
})).flatMap(((x$1) => x$1: @scala.unchecked match {
          case scala.Tuple2((a @ _), (b @ _)) => if (b.$eq$eq(10))
  Option(100)
else
  None.map(((c) => b.$plus(c)))
        }))
      };
      new $anon()
    }
  }
}

只拍摄你感兴趣的作品并提高可读性,你就明白了:

Option(5).map(a => {
  val b = a * 2
  (a, b)
}).flatMap(_ match {
  case (_, b) => 
    if (b == 10)
      Option(100)
    else
      None.map(c => b + c)
})

修改

正如评论中所报道的那样,从编译器输出字面翻译似乎突出了如何呈现desugared表达式的错误。总和应map表示if表达式的结果,而不是None分支中的else

Option(5).map(a => {
  val b = a * 2
  (a, b)
}).flatMap(_ match {
  case (_, b) => 
    (if (b == 10) Option(100) else None).map(c => b + c)
})

向编译器团队询问这是否是一个错误可能是值得的。