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

时间:2015-01-24 13:17:59

标签: scala lambda anonymous-function partial-application scala-placeholder-syntax

我使用_作为占位符来创建匿名函数,问题是我无法预测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)) 

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

2 个答案:

答案 0 :(得分:9)

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

  1. 如果下划线是方法的参数,那么范围将之外的那个方法,否则相应的规则如下;
  2. 如果下划线位于由()或{}分隔的表达式内,则将使用包含下划线的最里面的这样的分隔符;
  3. 所有其他条件相同,将使用最大的表达式。
  4. 因此,根据规则#1而不是println((x: Int) => x),范围将放在({)} println之外。

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

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

答案 1 :(得分:4)

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

唯一的规则是正确包含下划线的 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$1: Int) => List(1, 2, 3).foreach(println((x$1: Int))))

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

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

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