Scala编译器说“错误:预期的标识符,但找到整数文字。” for()not {}

时间:2013-07-14 13:42:12

标签: scala

为什么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)

3 个答案:

答案 0 :(得分:8)

写作时

{i:Int =&gt; 2 * i}

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

语法为the ResultExpr here。请注意,在参数名称之后允许使用类型,如上所述。

这与an Expr不同。

以下是差异的一个例子:

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

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

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

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

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

这些作品中的Bindings(在规范中)是普通的parens中的params列表,可能有类型。如果有一个以上的param,你必须提供parens:

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

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

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)

parens中的js只是一个值,显然不是一个参数,类型是类型归属。该值可以是修复后的表达式,因此可以(或者更确切地说,使用有关修复后操作符语法的功能警告进行编译):

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

由于认为parens和花括号在某种程度上是可交换的,所以会产生一种混乱。但我发现将括号视为BlockExprs是明确的,这就是它们的本质。然后更容易记住,例如,当你在函数应用程序中使用大括号时,没有魔法:你只提供了一个block

一个块是一系列副作用语句,后跟一个结果语句。对于功能程序员来说,这可能是显而易见的。但它澄清了你的大括号中的最后一件事是ResultExpr

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

答案 1 :(得分: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

但是,似乎Scala没有用于创建函数文字的{c:Char => blabla}表单的特殊规范。

我认为这是因为在类定义中有{some_var: SomeType => *Class Definition* }形式用this引用some_var并添加this类型的下限。这会导致Scala词法分析器将{之后的单个类型参数视为形式参数,并将=>之后的语句视为实际正文。请注意,您 CAN NOT 创建一个包含2个参数的函数,例如{c: Char, d:Char => 1}

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

答案 2 :(得分:1)

我已经为Scala 2.9挖掘了The Scala Language Specification,并发现第6.23部分描述了如何定义匿名函数:

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

正如您所看到的,Bindings需要放在两个周围的括号内。因此,如果您的匿名函数定义参数的类型,则具有以这种方式定义:

(c:Char) => 1

对地图的调用应该是这样的:

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

同样在参考文档的相同部分,您可以找到:

  

如果匿名函数(x:T)=&gt; e使用单个类型参数   作为块的结果表达式出现,可以缩写为   x:T =&gt;即

所以它说如果匿名函数被定义为代码块中的最后一个表达式,并且具有exacly一个参数,则可以使用缩写语法来定义匿名函数 - 不带括号。因此,在您的情况下,您可以编写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)

此外,文档末尾的changelog(版本2。1。7(2006年7月19日)中的更改)对此进行了总结:

关闭语法

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

x: T => E

仅在括在括号中时有效,即 { x: T => E }。以下是非法的,因为它可能被读作类型为T =&gt;的值x; E:

val f = x: T => E

法律选择是:

val f = { x: T => E }
val f = (x: T) => E