Scala中方法和函数的区别

我阅读Scala函数(Scala的另一个游览的一部分)。 他在那篇文章中写道:

方法和功能不是一回事

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


吉姆在他的博客文章中有很多内容,但我在这里发布简报以供参考。

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

函数类型 (大致)是一种形式(T1,...,Tn)=> U的类型,它是标准库中特征FunctionN N的缩写。 匿名函数方法值具有函数类型,函数类型可以用作值,变量和函数声明和定义的一部分。 实际上,它可以是方法类型的一部分。

方法类型是一种非值类型 。 这意味着没有价值 - 没有对象,没有实例 - 带有方法类型。 如上所述, 方法值实际上具有函数类型 。 一个方法类型是一个def声明 - 除了它的主体外,关于def所有内容。

值声明和定义以及变量声明和定义valvar声明,包括类型和值 - 可以分别是函数类型匿名函数或方法值 。 请注意,在JVM上,这些(方法值)是用Java调用“方法”的方式实现的。

函数声明是一个def声明,包括类型和正文。 类型部分是方法类型,而主体是表达式或块。 这也通过Java调用“方法”的方式在JVM上实现。

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

这就是规格说明的内容,所以让我先说一下: 我们不使用这个术语! 它导致所谓的“函数声明”(它是程序的一部分(第4章 - 基本声明))和“匿名函数”(它是一个表达式)和“函数类型”之间的混淆,好型 - 一种特质。

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

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

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

让我们看看这些特征之一的类型签名:

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

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

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

这告诉我们所有人都知道这一点。 函数有一个apply方法,它接收T1,T2,...,TN类型的N个参数,并返回R类型的东西。 它在接收到的参数上是相反的变体,并且结果是共变的。

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

现在,一个方法和一个函数有什么相似之处? 那么,如果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) => R类型签名。 例如:

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

方法和功能之间的另一个相似之处在于前者可以很容易地转换为后者:

val f = m _

Scala会扩展它,假设m类型是(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类来减少类的大小。

注意,不能转换 - 从一个函数转换为一个方法。

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

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

我认为这几乎涵盖了所有内容,但我很乐意补充这一点,并回答任何可能存在的问题。


方法和功能之间的一个很大的实际区别是return方式。 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

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

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

链接地址: http://www.djcxy.com/p/86189.html

上一篇: Difference between method and function in Scala

下一篇: Scala how can I count the number of occurrences in a list