这个例子来自于Beginning Scala,但它对我来说并没有得到足够的解释。
val f: Int => String = x => "Dude: "+x
我真的有两个问题:
1)第一个示例是否与下面的代码相同:
val f = (x:Int) => "Dude: "+x
2)如果是这样,有人可以详细说明,并稍微剖析第一个例子。 String = x部分抛弃了我。我不知道如何阅读声明
答案 0 :(得分:9)
他们是一样的。您正在以两种不同的方式为您进行scala的类型推断。
在val f: Int => String = x => "Dude: "+x
中,=符号之前的部分是您明确声明类型为f
的{{1}}的位置(即您说f是一个接受Int类型输入的函数)并返回一个String)
在=的右侧,你要为f赋值。值(根据您的声明)必须是一个接受Int并返回String的函数。
事实上,你说f等于Int => String
。
scala编译器的类型推断在这里工作,因为它理解x必须是一个Int,因为你已经将f声明为从Int到String的函数。
在x => "Dude: "+x
中,您通过查看分配给它的值,让scala的类型推断“猜测”f的类型。这个值(在=的右侧)是一个函数,它接受一个Int(因为你明确地说val f = (x:Int) => "Dude: "+x
)并返回一个String(因为你将x添加到“Dude:”,这是一个字符串,所以string + x的结果必须是一个字符串本身。)
注意:如果您在REPL中尝试两个版本,您会注意到在一种情况下,您将f定义为(Int)=>字符串,另一个为(Int)=> java.lang.String中
它们(从scala 2.8.1开始)是同样的事情:scala的字符串定义(在Predef.scala中)只是x:Int
。这可能是针对.NET框架的scala版本(其中String可能是.NET的System.String的别名),因此您可以始终在scala程序中编写String。
答案 1 :(得分:5)
好的,我们走了:
line 1: val f: Int => String
作为函数声明读取一个带有签名的值,该签名将类型Int
的第一个输入值映射到类型String
的输出值。
line 1: ... = x => "Dude: " + x
使用lambda(或函数文字)作为函数定义读取,该lambda接受一个名为x的参数,将其附加到字符串"Dude: "
并返回它。此定义分配给值f
。由于值f
是使用Int => String
声明的,因此当编译器推断其类型时,x
将采用类型Int
。在Scala中,函数体的最后一个表达式是return语句,在这种情况下恰好是String
,它符合我们的声明。此外,在Java和Scala中,运算符+
被重载。当您尝试将String
添加到Int
时,结果将是字符串值和整数的字符串连接。
现在看看这个:
line: 2 val f = (x:Int) => "Dude: " + x
这一次,f
没有用类型声明,它的类型是从它的定义(x:Int) => "Dude: " + x
推断出来的,它是一个函数文字,它带有一个名为x
的参数类型Int
并返回String
。应该清楚的是,现在这两个版本是等价的。
现在,作为练习,尝试编写一些使用关键字def
的版本。完成后,在REPL中键入所有f()
,并观察其签名。到那时应该很清楚。
答案 2 :(得分:2)
它是一样的。您的两个版本的完整版本是:
val f: Int => String = (x: Int) => "Dude:" + x
语言规范的第6.23节说如果参数的期望的类型是已知的,那么你可以省略匿名函数中参数的类型,结果如下:
val f: Int => String = (x) => "Dude:" + x
然后,引用规范:“在单个无类型形式参数的情况下,(x)=> e可以缩写为x => e。”这将导致:
val f: Int => String = x => "Dude:" + x
...这是你的第一个版本。
但是,如果执行指定匿名函数中的参数类型,则在大多数情况下Scala将能够推断出匿名函数的类型,因此无需指定明确的函数类型。这并不意味着这样做是错误的。这只是多余的。因此,如果您删除包含匿名函数的变量的类型定义,则会获得第二个定义。
val f = (x: Int) => "Dude:" + x
你会期望 - 鉴于此 - 也应该可以像这样定义你的函数:
scala> val f: Int => String = x: Int => "Dude:" + x
<console>:1: error: identifier expected but string literal found.
val f: Int => String = x: Int => "Dude:" + x
^
但是,这会使编译器感到困惑。 (规范说明了一些内容。我还没有找到它。)如果做想要指定匿名函数的参数类型(正如我所说,没有理由要做到这一点,你想省略括号,那么这将有效:
val f: Int => String = { x: Int => "Dude:" + x }