Scala编译器说;错误:需要标识符,但找到整数文字。";for()不是{}

Scala编译器说;错误:需要标识符,但找到整数文字。";for()不是{},scala,Scala,为什么Scala2.11.0-M3编译器会给我错误:需要标识符,但找到了整数文字。当使用圆括号()时,它会用花括号{}进行良好编译 $ 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> v

为什么Scala
2.11.0-M3
编译器会给我
错误:需要标识符,但找到了整数文字。
当使用圆括号
()
时,它会用花括号
{}
进行良好编译

$ 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)

编写表达式
c:Char=>1
时,Scala编译器将其视为
c:(Char=>1)
,即类型为
Char=>1
的函数变量
c
。但是,
1
不是有效的类型(或类型变量),因此Scala编译器会抱怨。因此,您需要编写
s.toList映射((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\u var:SomeType=>*Class Definition*}
形式来引用
this
,并添加
this
类型的下限。这会导致Scala lexer将
{
之后的单个类型参数视为形式参数,并将
=>
之后的语句视为实际体。请注意,不能创建具有两个参数的函数,如
{c:Char,d:Char=>1}

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

当您编写

{i:Int=>2*i}

大括号是一个块,其中的内容是块的结果表达式

语法为。请注意,参数名称后面允许有类型,如上所述

这与实际情况不同

下面是一个不同的例子:

scala> val is = List(1,2,3)
is: List[Int] = List(1, 2, 3)
块中的第一条语句有一个函数文本,如果我们指定类型,该表达式需要参数。最后一条语句是块的结果表达式,不需要参数:

scala> is map { val f = (i: Int) => 2*i; i: Int => 2*f(i) }
res0: List[Int] = List(4, 8, 12)
有时您不需要指定类型,因为它是从预期的类型推断出来的,并且在表达式中没有类型的情况下,您可以省略参数:

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)
这与下面的代码不同,下面的代码是由许多行代码组成的块,最后是函数literal。函数只返回
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> 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
显然只是一个值,而不是参数,类型是一种类型归属。该值可以是一个后修复表达式,因此它可以工作(或者更确切地说,编译时带有关于后修复运算符语法的功能警告):

更多关于这个答案的解释:

认为括号和花括号在某种程度上是可交换的,这会引起一类混乱。但我发现,将大括号视为
blockexpr
,这一点很清楚,这就是它们的本质。这样就更容易记住,例如,当你在函数应用程序中使用大括号时,没有什么神奇之处:你只是提供了一个

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

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

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

Expr ::= (Bindings | [‘implicit’] id | ‘_’) ‘=>’ Expr
ResultExpr ::= (Bindings | ([‘implicit’] id | ‘_’) ‘:’ CompoundType) ‘=>’ Block
Bindings ::= ‘(’ Binding {‘,’ Binding} ‘)’
Binding ::= (id | ‘_’) [‘:’ Type]
正如您所见,
绑定
需要放在两个括号内。因此,如果您的匿名函数定义了参数的类型,则它必须以以下方式定义

(c:Char) => 1
s.toList map({c:Char => 1})
对map的调用应该如下所示:

s.toList map((c:Char) => 1)
在参考文档的同一部分中,您还可以找到:

如果一个匿名函数(x:T)=>e和一个单一类型的参数 显示为块的结果表达式,可以缩写为 x:T=>e

所以它说,如果匿名函数被定义为代码块中的最后一个表达式,并且有一个参数,那么可以使用缩写语法来定义匿名函数——不带括号。因此,在您的例子中,您可以编写
c:Char=>1
,但只有当您将它放在代码块
{c:Char=>1}
。您可以通过以下方式调用映射函数:

(c:Char) => 1
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

我明白你为什么会像你一样猜测大括号,但事实上,看看规范并不难,参考我的答案。你的开场白仍然是误导性的或不精确的。(我需要括号吗?大括号是非函数语法的一部分?)可能是最好的建议
val f = x: T => E
val f = { x: T => E }
val f = (x: T) => E