管理下划线定义匿名函数的规则是什么?

What are the rules to govern underscore to define anonymous function?

我使用 _ 作为创建匿名函数的占位符,问题是我无法预测 Scala 将如何转换我的代码。更确切地说,它错误地确定了如何"large"我想要的匿名函数。

 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 将第一个转换为 "a big anonymous function":

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

工作的2号3号都改造成了我想要的样子

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

这是为什么?规则是什么?

确定下划线范围的简单规则:

  1. 如果下划线是一个方法的参数,那么作用域将那个方法之外,否则分别遵循下面的规则;
  2. 如果下划线在由 () 或 {} 分隔的表达式中,将使用包含下划线的最内层分隔符;
  3. 在所有其他条件相同的情况下,将使用可能的最大表达式。

因此,根据规则 #1,范围将放在(包括)println.

之外,而不是 println((x: Int) => x)

根据规则 #2,后两个示例将具有由括号分隔的函数,因此 (x => println(x: Int)).

根据规则 #3,第一个示例将是整个表达式,因为没有定界括号。

我认为索布拉尔先生的回答是错误的。实际规则可以在 Scala Language Reference,第 6.23 节,小标题 "Placeholder Syntax for Anonymous Functions."

中找到

唯一的规则是 innermost 正确包含下划线的表达式定义了匿名函数的范围。这意味着 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)
                      ^

为了更好地理解事物,请这样调用 scalascala -Xprint:parser,在用户键入每个表达式后,将打印由解析器充实的表达式。 (还有很多垃圾,我将省略。)对于 Laskowski 的第一个示例,解析器理解的表达式是

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

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

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

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