在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声明和定义是val
和var
声明,包括types和值 – 可以分别是函数types和匿名函数或方法值 。 请注意,在JVM上,这些(方法值)是通过Java调用“methods”来实现的。
函数声明是一个def
声明,包括types和正文 。 types部分是方法types,而主体是expression式或块 。 这也是用JVM调用“方法”的方式实现的。
最后,一个匿名函数是一个函数types的实例(也就是特征FunctionN
N的一个实例),而一个方法的值也是一样的! 区别在于方法值是通过方法创build的,或者通过后缀下划线( m _
是与“函数声明”( def
) m
相对应的方法值),或者通过名为eta-expansion的进程来创build,就像从方法到function自动投射。
规格说明就是这样,所以让我先把这个术语说出来: 我们不使用这个术语! 它导致了所谓的“function声明” (这是程序的一部分(第4章 – 基本声明)和“匿名function” )之间的混淆, “匿名function”是一个expression式, “functiontypes”好型 – 一种特质。
下面的术语,由经验丰富的Scala程序员使用,从规范的术语上做了一个改变: 不是说函数声明 ,而是说方法 。 甚至方法声明。 此外,我们注意到价值声明和variables声明也是用于实际目的的方法。
所以,鉴于上述术语的变化,下面是对这个区别的实际解释。
函数是一个包含FunctionX
特征之一的对象,如Function0
, Function1
, Function2
等。它也可能包括PartialFunction
,它实际上扩展了Function1
。
让我们来看看这些特征之一的types签名:
trait Function2[-T1, -T2, +R] extends AnyRef
这个特征有一个抽象的方法(它也有一些具体的方法):
def apply(v1: T1, v2: T2): R
这告诉我们所有人都知道这件事。 一个函数有一个apply
方法,它接收T1 , T2 ,…, 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) => R
types的签名。 例如:
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会扩展,假设m
types是(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)