在Scala中的方法和function的区别

我阅读Scala函数另一个斯卡拉游览的一部分)。 他在那篇文章中说:

方法和function不是一回事

但他没有解释任何事情。 他想说什么?

吉姆在他的博客文章中已经有了很多这方面的内容 ,但是我在这里发布一个简报供参考。

首先,让我们看看Scala规范告诉我们什么。 第3章(types)告诉我们函数types (3.2.9)和方法types (3.3.1)。 第四章(基本声明)谈到价值声明和定义 (4.1), variables声明和定义 (4.2)以及函数声明和定义 (4.6)。 第6章(expression式)提到了匿名函数 (6.23)和方法值 (6.7)。 奇怪的是,函数值在3.2.9被说了一次,没有其他的地方。

函数types (大体上)是一种forms(T1,…,Tn)=> U的types ,它是标准库中特征FunctionN N的缩写。 匿名函数方法值具有函数types,函数types可以用作值,variables和函数声明和定义的一部分。 实际上,它可以是方法types的一部分。

方法types是一个非值types 。 这意味着没有价值 – 没有对象,没有实例 – 与方法types。 如上所述,一个方法值实际上有一个函数types 。 一个方法types是一个def声明 – 除了它的主体外,关于def所有东西。

值声明和定义variables声明和定义valvar声明,包括types和值 – 可以分别是函数types匿名函数或方法值 。 请注意,在JVM上,这些(方法值)是通过Java调用“methods”来实现的。

函数声明是一个def声明,包括types正文 。 types部分是方法types,而主体是expression式或块 。 这也是用JVM调用“方法”的方式实现的。

最后,一个匿名函数是一个函数types的实例(也就是特征FunctionN N的一个实例),而一个方法的值也是一样的! 区别在于方法值是通过方法创build的,或者通过后缀下划线( m _是与“函数声明”( defm相对应的方法值),或者通过名为eta-expansion的进程来创build,就像从方法到function自动投射。

规格说明就是这样,所以让我先把这个术语说出来: 我们不使用这个术语! 它导致了所谓的“function声明” (这是程序的一部分(第4章 – 基本声明)和“匿名function” )之间的混淆, “匿名function”是一个expression式, “functiontypes”好型 – 一种特质。

下面的术语,由经验丰富的Scala程序员使用,从规范的术语上做了一个改变: 不是说函数声明 ,而是说方法 。 甚至方法声明。 此外,我们注意到价值声明variables声明也是用于实际目的的方法。

所以,鉴于上述术语的变化,下面是对这个区别的实际解释。

函数是一个包含FunctionX特征之一的对象,如Function0Function1Function2等。它也可能包括PartialFunction ,它实际上扩展了Function1

让我们来看看这些特征之一的types签名:

 trait Function2[-T1, -T2, +R] extends AnyRef 

这个特征有一个抽象的方法(它也有一些具体的方法):

 def apply(v1: T1, v2: T2): R 

这告诉我们所有人都知道这件事。 一个函数有一个apply方法,它接收T1T2 ,…, TNtypes的N个参数,并返回typesR东西。 它接收到的参数是相反的,并且结果是一致的。

这个差异意味着Function1[Seq[T], String]Function1[List[T], AnyRef]的子types。 作为一个亚型意味着它可以用来代替它。 我们可以很容易地看到,如果我打算调用f(List(1, 2, 3))并期待AnyRef返回,上面两种types中的任何一种都可以工作。

现在,一个方法和一个函数有什么相似之处呢? 那么,如果f是一个函数,而m是范围的局部方法,则两者都可以这样调用:

 val o1 = f(List(1, 2, 3)) val o2 = m(List(1, 2, 3)) 

这些调用实际上是不同的,因为第一个只是一个语法糖。 斯卡拉扩展到:

 val o1 = f.apply(List(1, 2, 3)) 

当然,这是一个调用对象f的方法。 函数还有其他的语法糖:函数文字(其中两个,实际上)和(T1, T2) => Rtypes的签名。 例如:

 val f = (l: List[Int]) => l mkString "" val g: (AnyVal) => String = { case i: Int => "Int" case d: Double => "Double" case o => "Other" } 

方法和function之间的另一个相似之处是前者可以很容易地转换成后者:

 val f = m _ 

Scala会扩展,假设mtypes是(List[Int])AnyRef (Scala 2.7):

 val f = new AnyRef with Function1[List[Int], AnyRef] { def apply(x$1: List[Int]) = this.m(x$1) } 

在Scala 2.8中,它实际上使用AbstractFunction1类来减小类的大小。

注意一个不能转换的方式 – 从一个函数到一个方法。

然而,方法有一个很大的优势(好吧,两个 – 它们可以稍微快一些):它们可以接收types参数 。 例如,上面的f可以指定它接收到的List的types(例子中的List[Int] ), m可以参数化它:

 def m[T](l: List[T]): String = l mkString "" 

我认为这几乎涵盖了一切,但我很乐意补充这个问题,回答任何可能存在的问题。

一个方法和一个函数之间的一个很大的实际区别是什么return手段。 只返回一个方法的返回值。 例如:

 scala> val f = () => { return "test" } <console>:4: error: return outside method definition val f = () => { return "test" } ^ 

从方法中定义的函数返回一个非本地返回:

 scala> def f: String = { | val g = () => { return "test" } | g() | "not this" | } f: String scala> f res4: String = test 

而从本地方法返回只从该方法返回。

 scala> def f2: String = { | def g(): String = { return "test" } | g() | "is this" | } f2: String scala> f2 res5: String = is this 

函数可以用参数列表调用一个函数来产生一个结果。 一个函数有一个参数列表,一个主体和一个结果types。 属于类,特征或单例对象成员的函数称为方法 。 在其他函数中定义的函数被称为本地函数。 具有单元结果types的函数称为过程。 源代码中的匿名函数称为函数文字。 在运行时,函数文字被实例化为称为函数值的对象。

Scala编程第二版。 Martin Odersky – Lex Spoon – Bill Venners

假设你有一个列表

 scala> val x =List.range(10,20) x: List[Int] = List(10, 11, 12, 13, 14, 15, 16, 17, 18, 19) 

定义一个方法

 scala> def m1(i:Int)=i+2 m1: (i: Int)Int 

定义一个函数

 scala> (i:Int)=>i+2 res0: Int => Int = <function1> scala> x.map((x)=>x+2) res2: List[Int] = List(12, 13, 14, 15, 16, 17, 18, 19, 20, 21) 

方法接受参数

 scala> m1(2) res3: Int = 4 

用val定义函数

 scala> val p =(i:Int)=>i+2 p: Int => Int = <function1> 

function参数是可选的

  scala> p(2) res4: Int = 4 scala> p res5: Int => Int = <function1> 

对方法的争论是强制性的

 scala> m1 <console>:9: error: missing arguments for method m1; follow this method with `_' if you want to treat it as a partially applied function 

检查下面的教程 ,解释如何传递其他差异,例如使用方法Vs函数的差异的其他例子,使用函数作为variables,创build返回函数的函数

函数不支持参数默认值。 方法呢。 从方法转换为函数会丢失参数默认值。 (Scala 2.8.1)