在 Scala 中从 for 循环块返回一个值

In Scala returning a value from a for loop block

在书 "Scala for the impatient" 中,第 16 页说

In Scala, a { } block contains a sequence of expressions, and the result is also an expression. The value of the block is the value of the last expression.

OK,那我们来创建一个区块,让区块的最后一个值赋值:

scala> val evens = for (elem <- 1 to 10 if elem%2==0) {
     |   elem
     | }
val evens: Unit = ()

我本以为 evens 至少是序列的最后一个值(即 10)。但为什么不呢?

您需要 yield 值,然后它是一个 for 表达式:

val evens = for (elem <- 1 to 10 if elem % 2 == 0) yield elem

如果没有它,它只是一个语句(不 return 任何东西)并被翻译成 foreach

P.S.: 当然这将 return 满足谓词的所有元素的集合,而不是最后一个。

如有疑问,只需 运行 通过类型检查器查看幕后情况

scala -Xprint:typer -e 'val evens = for (elem <- 1 to 10 if elem%2==0) { elem }'

揭示

val evens: Unit = 
  scala.Predef
    .intWrapper(1)
    .to(10)
    .withFilter(((elem: Int) => elem.%(2).==(0)))
    .foreach[Int](((elem: Int) => elem))

我们看到 foreach 是链中的最后一步,它的签名是

def foreach[U](f: A => U): Unit

我们在哪里看到它 returns Unit。您甚至可以通过执行以下命令直接从 REPL 中执行此操作

scala> :settings -Xprint:typer

现在您将在解释 Scala 表达式的同时获得实时脱糖。您甚至可以更进一步,获取 JVM 字节码本身

scala> :javap -

For-comprehensions 是 Scala 中一些最普遍的语法糖,所以我建议尽可能多地练习它们,也许尝试同时在它们的 suggared 和 desugared from 中编写它们,直到它点击: https://docs.scala-lang.org/tutorials/FAQ/yield.html

Unit 是您书中所述规则的例外。 Unit 基本上说 "ignore whatever type the block would have returned because I only intended to execute the block for the side effects." 否则,为了让它进行类型检查,你必须在任何应该 return [=11= 的块的末尾添加一个单位值]:

val evens = for (elem <- 1 to 10 if elem%2==0) {
  elem
  ()
}

这种类型信息的丢弃是人们倾向于在 Scala 中避免命令式 for 循环和类似循环的原因之一。