Scala中的"<-"用于推导表达式。

26
我发现Scala总是有一个“自然的解释”来解释任何事情。总是像“哦,但那只是在这个对象上调用一个函数,并且使用这个和那个参数”。从某种意义上说,没有什么是真正的编译器魔法,就像我们从其他语言中知道的那样。
我的问题是关于以下代码中使用的<-运算符:
for(i <- 0 to 10) println(i)

在这个例子中,我可以看到它被重新编写成类似于:

0.to(10).foreach((i:Int)=>println(i))

但这并没有解释在foreach函数内部匿名函数中如何传递i。在编写i时,它既不是一个对象,也没有被声明为变量。那么它是什么,又是如何传递到foreach的内部的呢?

我的猜测是我终于发现了一些实际上是编译器魔法的东西。

感谢您的时间。

澄清一下:我的问题是:由于并不是可以作为函数调用的对象,所以代码第一行中的<-运算符是如何工作的。

3个回答

64
为了补充Dave的回答,这里提供了一个Scala语言规范中“for-comprehensions”的翻译模式:
一个 comprehension for (enums) yield e 对枚举器 enums 生成的每个绑定求值表达式 e。枚举器序列始终以生成器开头;可以跟随进一步的生成器、值定义或保护条件。
生成器 p <- e 从表达式 e 中产生绑定,该表达式以某种方式与模式 p 匹配。值定义 val p = e 将值名称 p(或模式 p 中的多个名称)绑定到评估表达式 e 的结果。保护条件 if e 包含限制枚举绑定的布尔表达式。
生成器和保护条件的确切含义由翻译为调用四个方法的方式定义:mapfilterflatMapforeach。这些方法可以针对不同的载体类型以不同的方式实现。
翻译方案如下。首先,将每个生成器 p <- e(其中 p 不是对于 e 的类型不可反驳的(§8.1))替换为:
 p <- e.filter { case p => true; case _ => false }

然后,重复应用以下规则,直到消除所有的 comprehension:
  • 将 for-comprehension for (p <- e) yield e0 翻译为 e.map { case p => e0 }

  • 将 for-comprehension for (p <- e) e0 翻译为 e.foreach { case p => e0 }

  • 将 for-comprehension for (p <- e; p0 <- e0 . . .) yield e00(其中 . . . 是一个(可能为空)的生成器或保护条件序列)翻译为:
    e.flatMap { case p => for (p0 <- e0 . . .) yield e00 }

  • 将 for-comprehension for (p <- e; p0 <- e0 . . .) e00(其中 . . . 是一个(可能为空)的生成器或保护条件序列)翻译为:
    e.foreach { case p => for (p0 <- e0 . . .) e00 }

  • 生成器 p <- e 后跟保护条件 if g 被翻译为单个生成器:
    p <- e.filter((x1, . . . , xn) => g )
    其中 x1,. . . ,xnp 的自由变量。

  • 生成器 p <- e 后跟值定义 val p0 = e0 被翻译为以下值对的生成器,其中 xx0 是新名称:

    val (p, p0) <- 
      for(x@p <- e) yield { val x0@p0 = e0; (x, x0) }
    

1
好的,第一遍阅读后我并不完全理解,但这很有趣 :-) 你是从哪里得到这个的? - Felix
@Felix:正如我在答案开头所说的,这是来自语言规范(可从www.scala-lang.org下载)。 - missingfaktor
语言规范链接:https://scala-lang.org/files/archive/spec/2.12/06-expressions.html#for-comprehensions-and-for-loops - davidvandebunte

18

<- 是一个语言定义的关键字符号,而=>也是如此,但与->形成鲜明对比(后者是一个已定义的符号)。由于它是基本Scala语法的一部分,它可以用来创建绑定(例如您示例中的i),这是用户自定义结构所无法做到的。


这似乎是答案。如果可能的话,我建议您记录下来,因为现在有点突然。 - Felix
4
@Felix:这在规范文档中有记录。市面上几乎所有的Scala书籍都有涵盖它。 - missingfaktor
哦,是的,我还没有买到书。正在等待2.8版的权威指南。 - Felix
1
这在2.8中并不是新的。我只是在2.7时代开始学习这门语言,但我相信它的历史要追溯得更久远,可能可以追溯到这门语言的起源。 - Randall Schulz
是的...这仍然无法改变我正在等待权威的2.8书籍的事实 :-) - Felix

7
在这种情况下,它确实是一些编译器魔法。从for-comprehension转换为filter/map/flatmap形式是一种特殊的解糖技术,就像更新和应用方法的特殊形式的转换一样。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接