14

为什么 Scala编译器在使用圆括号时2.11.0-M3给我,而它用花括号编译得很好?error: identifier expected but integer literal found.(){}

$ scala
Welcome to Scala version 2.11.0-M3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_21).
Type in expressions to have them evaluated.
Type :help for more information.

scala> val s = "this is a string"
s: String = this is a string

scala> s.toList map (c:Char => 1)
<console>:1: error: identifier expected but integer literal found.
       s.toList map (c:Char => 1)
                               ^

scala> s.toList map {c:Char => 1}
res7: List[Int] = List(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)

当匿名函数的左侧在另一对圆括号内时,它也可以用圆括号很好地编译。为什么?

scala> s.toList map ((c:Char) => 1)
res8: List[Int] = List(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
4

3 回答 3

9

当你写

{ i: Int => 2 * i }

大括号是一个块,里面的东西是块的结果表达式。

语法是这里的 ResultExpr。请注意,参数名称后允许使用类型,如上。

这与没有的 Expr不同。

这是差异的示例:

scala> val is = List(1,2,3)
is: List[Int] = List(1, 2, 3)

块中的第一条语句有一个函数文字,如果我们指定类型,则需要括号的 Expr。最后一条语句是块的结果表达式,不需要括号:

scala> is map { val f = (i: Int) => 2*i; i: Int => 2*f(i) }
res0: List[Int] = List(4, 8, 12)

有时您不需要指定类型,因为它是从预期类型推断出来的,并且在 Expr 中没有类型,您可以省略括号:

scala> is map { val f: Int=>Int = i => 2*i; i: Int => 2*f(i) }

这些产品中的绑定(在规范中)是您在括号中的普通参数列表,可能具有类型。使用多个参数,您必须提供括号:

scala> is reduce { val f = (i:Int,j:Int) => i+j; (i:Int,j:Int) => 2*f(i,j) }
res2: Int = 18

有时你会看到一大块代码作为函数的参数。作为块的结果表达式,这是一个很大的函数文字,这就是为什么您会看到参数放在前面,没有括号或大括号:

scala> is map { i =>  val j = 2 * i; /* lots of LOC */ ; j }
res7: List[Int] = List(2, 4, 6)

这与下面的代码不同,它是多行代码块,最后是函数文字。该函数只返回j或 2:

scala> is map { val j = 2; /* lots of LOC */ ; _ => j }
res8: List[Int] = List(2, 2, 2)

所以我们知道你不能写以下内容:

scala> is map (is: Int => 2)
<console>:1: error: identifier expected but integer literal found.
       is map (is: Int => 2)
                          ^

在这种情况下,什么样的标识符是有意义的?怎么样:

scala> is map (is: Int => Int)

这产生了令人愉快的结果(剧透警报):

java.lang.IndexOutOfBoundsException: 3

这有效:

scala> val js = List(0,1,2)
js: List[Int] = List(0, 1, 2)

scala> js map (js: Int => Int)
res0: List[Int] = List(0, 1, 2)

jsin parens 只是一个值,显然不是参数,类型是类型归属。该值可以是一个后缀表达式,所以这有效(或者更确切地说,编译时带有关于后缀运算符语法的功能警告):

scala> js map (js init: Int => Int)
warning: there were 1 feature warning(s); re-run with -feature for details
java.lang.IndexOutOfBoundsException: 2

此答案的更多解释:

https://stackoverflow.com/a/13873899/1296806

由于认为括号和花括号可以以某种方式交换,会引起一类混淆。但是我发现将大括号视为是很清楚的BlockExprs,这就是它们的含义。然后更容易记住,例如,当您在函数应用程序中使用大括号时,并没有什么神奇之处:您只是提供了一个block

块是一堆副作用语句,后面跟着一个结果语句。这对函数式程序员来说是显而易见的,也许。但它澄清了大括号中的最后一件事是ResultExpr.

(脚注:类主体的大括号当然是不同的。)

于 2013-07-14T18:35:29.427 回答
3

编写表达式c:Char => 1时,Scala 编译器将其视为c:(Char => 1),即c类型为 的函数变量Char => 1。但是,1它不是有效的类型(或类型变量),因此 Scala 编译器会抱怨。所以你需要编写s.toList map ((c:Char) => 1)来帮助编译器摆脱类型关联。

{c:Char => 1}创建一个函数字面量,即一个接受 char 并返回 1 的函数。所以

s.toList map {c:Char => 1}

是相同的

s.toList map({c:Char => 1})

// or
val fn = {c: Char => 1}
s.toList map fn

{c:Char => blabla}但是,似乎 Scala对创建函数字面量的形式没有特殊规范。

我认为这是因为{some_var: SomeType => *Class Definition* }类定义中有表单可以引用thissome_var添加this. 这导致 Scala 词法分析器将单个类型参数 after{视为形式参数,并将语句 after=>视为实际主体。请注意,您不能像这样创建具有 2 个参数的函数{c: Char, d:Char => 1}

更新:正如som-snytt所评论的,结果表达式导致大括号版本被评估为函数结果。

于 2013-07-14T14:24:53.250 回答
1

我深入研究了 Scala 2.9的 Scala 语言规范,发现第 6.23 部分描述了如何定义匿名函数:

Expr ::= (Bindings | [‘implicit’] id | ‘_’) ‘=>’ Expr
ResultExpr ::= (Bindings | ([‘implicit’] id | ‘_’) ‘:’ CompoundType) ‘=>’ Block
Bindings ::= ‘(’ Binding {‘,’ Binding} ‘)’
Binding ::= (id | ‘_’) [‘:’ Type]

如您所见Bindings,需要放在两个圆括号内。因此,如果您的匿名函数定义了参数的类型,则必须以这种方式定义:

(c:Char) => 1

对 map 的调用应该是这样的:

s.toList map((c:Char) => 1)

您还可以在参考文档的同一部分找到:

如果带有单个类型参数的匿名函数 (x: T) => e 作为块的结果表达式出现,则可以缩写为 x: T => e。

所以它说如果匿名函数被定义为代码块中的最后一个表达式,并且只有一个参数,你可以使用缩写语法来定义匿名函数 - 没有括号。因此,在您的情况下,您可以编写c:Char => 1,但前提是您将其放在代码块{c:Char => 1}中。你可以这样调用 map 函数:

s.toList map({c:Char => 1})

或使用不带括号的缩写语法:

s.toList map {c:Char => 1}

另一方面,当我们回顾匿名函数规范时:

Expr ::= (Bindings | [‘implicit’] id | ‘_’) ‘=>’ Expr

我们可以看到,如果我们不想指定参数类型,我们可以定义您的匿名函数,而无需通过以下两种方式进行参数绑定:

s.toList map (c => 1)
// or
s.toList map (_ => 1)

此外,这在文档末尾的更改日志中进行了总结(版本 2.1.7 中的更改(2006 年 7 月 19 日)):

闭包语法

闭包的语法略有限制(第 6.23 节)。表格

x: T => E

仅当用大括号括起来时才有效,即 { x: T => E }. 以下是非法的,因为它可能被读取为使用类型 T => E 键入的值 x:

val f = x: T => E

合法的替代方案是:

val f = { x: T => E }
val f = (x: T) => E
于 2013-07-15T08:01:49.857 回答