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