管理下划线定义匿名函数的规则是什么?
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,范围将放在(包括)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)
^
为了更好地理解事物,请这样调用 scala
:scala -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
需要一个函数。一旦你看到解析树,就很容易看出这是正确的,但(对我来说)不清楚为什么解析树是这样的。
我使用 _
作为创建匿名函数的占位符,问题是我无法预测 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,范围将放在(包括)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)
^
为了更好地理解事物,请这样调用 scala
:scala -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
需要一个函数。一旦你看到解析树,就很容易看出这是正确的,但(对我来说)不清楚为什么解析树是这样的。