8

_用作创建匿名函数的占位符,问题是我无法预测 Scala 将如何转换我的代码。更准确地说,它错误地确定了我想要的匿名函数有多“大”。

 List(1,2,3) foreach println(_:Int)   //error !
 List(1,2,3) foreach (println(_:Int)) //work
 List(1,2,3) foreach(println(_:Int))  //work

使用-Xprint:typer我可以看到 Scala 将第一个转换为“一个大的匿名函数”:

x$1 => List(1,2,3) foreach(println(x$1:Int))

工作的第 2 次第 3 次正确转变为我想要的。

... foreach (x$1 => println(x$1:Int)) 

为什么这个?有什么规律?

4

2 回答 2

10

Simple rules to determine the scope of underscore:

  1. If the underscore is an argument to a method, then the scope will be outside that method, otherwise respective the rules below;
  2. If the underscore is inside an expression delimited by () or {}, the innermost such delimiter that contains the underscore will be used;
  3. All other things being equal, the largest expression possible will be used.

So, by the rule #1, instead of println((x: Int) => x), the scope will be placed outside (including) println.

By rule #2, the latter two examples will have the function delimited by parenthesis, so (x => println(x: Int)).

By rule #3, the first example will be the whole expression, as there are no delimiting parenthesis.

于 2015-01-24T13:38:38.747 回答
4

我相信索布拉尔先生的回答是不正确的。实际规则可以在Scala Language Reference的第 6.23 节的小标题“匿名函数的占位符语法”中找到。

唯一的规则是正确包含下划线的最里面的表达式定义了匿名函数的范围。这意味着 Sobral 先生的前两条规则是正确的,因为方法调用是一个表达式,给表达式加上括号并不会改变其含义。但是第三条规则与事实相反:在所有其他条件相同的情况下,将使用有意义的最小表达式。

不幸的是,我对 Laskowski 先生在他的第一个例子中观察到的行为的解释有点牵强和推测。什么时候

List(1,2,3) foreach println(_:Int)

在 Scala 的 read-eval-print 循环中输入。错误信息是:

error: type mismatch;
 found   : Unit
 required: Int => ?
              List(1,2,3) foreach println(_:Int)
                                         ^

如果您稍微改变示例:

List(1,2,3).foreach println(_:Int)

错误信息更容易理解——

error: missing arguments for method foreach in class List;
follow this method with `_' if you want to treat it as a partially applied function
          List(1,2,3).foreach println(_:Int)
                      ^

为了更好地理解事情,scala这样调用: scala -Xprint:parser,在用户输入每个表达式之后,会导致解析器充实的表达式被打印出来。(还有很多垃圾,我将省略。)对于 Laskowski 的第一个示例,解析器理解的表达式是

((x$1: Int) => List(1, 2, 3).foreach(println((x$1: Int))))

对于第二个示例,解析器的版本是

((x$1: Int) => List(1, 2, 3).foreach.println((x$1: Int)))

显然,范围规则是在表达式结构完全充实之前应用的。在这两种情况下,解析器都猜测最小的表达式从 List 开始,即使插入括号后就不再正确了。在第二个例子中,除了这个假设之外,它还假设,因为println是一个标识符,foreach println是一个方法链,第一个没有参数。然后在处的错误foreach之前捕获错误println,将其屏蔽。错误println在于它的结果是单位,并且foreach需要一个函数。一旦您看到解析树,就很容易看出这是正确的,但是(对我而言)不清楚为什么解析树是这样的。

于 2015-08-04T17:17:42.167 回答