我使用_
作为占位符来创建匿名函数,问题是我无法预测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))
为什么这样?这是什么规则?
答案 0 :(得分:9)
确定下划线范围的简单规则:
因此,根据规则#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)
^
为了更好地理解事情,请调用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开始,即使插入的parens不再是真的。在第二个示例中,除了该假设之外,它假设,因为println
是标识符,foreach println
是方法链,第一个没有参数。然后在foreach
处的错误之前捕获println
处的错误,屏蔽它。 println
的错误是其结果为单位,foreach
需要一个函数。一旦你看到解析树,就很容易看出这是正确的,但是(对我来说)解析树的原因并不清楚。