在scala中解析一个简单的函数声明

时间:2011-03-14 10:34:16

标签: scala

这个例子来自于Beginning Scala,但它对我来说并没有得到足够的解释。

val f: Int => String = x => "Dude: "+x

我真的有两个问题:

1)第一个示例是否与下面的代码相同:

val f = (x:Int) => "Dude: "+x

2)如果是这样,有人可以详细说明,并稍微剖析第一个例子。 String = x部分抛弃了我。我不知道如何阅读声明

3 个答案:

答案 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的类型。这个值(在=的右侧)是一个函数,它接受一个I​​nt(因为你明确地说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 }