Answers:
Jim在他的博客文章中对此进行了详细介绍,但我在此处发布了一个简介以供参考。
首先,让我们看看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)=>Ü,这对于该性状的简写FunctionN
在标准库。匿名函数和方法值具有函数类型,并且函数类型可用作值,变量和函数的声明和定义的一部分。实际上,它可以是方法类型的一部分。
甲方法类型是一个非值类型。这意味着没有方法类型的值-没有对象,没有实例。如上所述,方法值实际上具有功能类型。方法类型是一个def
声明-有关def
其主体的所有内容。
值声明和定义以及变量声明和定义是val
和var
声明,包括类型和值,它们可以分别是函数类型和匿名函数或方法值。注意,在JVM上,这些(方法值)是用Java所谓的“方法”实现的。
甲函数声明 是一个def
声明,包括类型和身体。类型部分是方法类型,主体是表达式或块。这也通过Java所谓的“方法”在JVM上实现。
最后,匿名函数是函数类型的实例(即trait的实例FunctionN
),而方法值是同一回事!区别在于,方法值是通过方法创建的,方法是后缀下划线(m _
是对应于“函数声明”(def
)的方法值m
),也可以是称为eta-expansion的过程,这类似于方法的自动转换发挥作用。
规范就是这么说的,所以让我先说一下:我们不使用该术语!这导致程序的一部分(第4章-基本声明)即所谓的“函数声明”与作为表达式的“匿名函数”与“函数类型”(即,一个类型-一个特质。
下面的术语,由经验丰富的Scala程序员使用,与规范的术语相比有一个变化:我们说方法而不是说函数声明。甚至方法声明。此外,我们注意到值声明和变量声明也是实用的方法。
因此,鉴于上述术语的变化,以下是对该区别的实用解释。
甲函数是一个对象,包括所述的一个FunctionX
特征,诸如Function0
,Function1
,Function2
等。它可以被包括PartialFunction
为好,这实际上延伸Function1
。
让我们看一下这些特征之一的类型签名:
trait Function2[-T1, -T2, +R] extends AnyRef
此特征具有一种抽象方法(也具有一些具体方法):
def apply(v1: T1, v2: T2): R
这就告诉我们所有人都应该知道这一点。甲函数有一个apply
接收方法Ñ的类型参数T1,T2,...,TN,并且返回一些类型的R
。它在接收到的参数上是反变量,在结果上是协变量。
这种差异意味着a 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))
这些调用实际上是不同的,因为第一个仅仅是语法糖。Scala将其扩展为:
val o1 = f.apply(List(1, 2, 3))
当然,这是对object的方法调用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 _
阶将扩大的是,假设m
类型是(List[Int])AnyRef
成(阶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 ""
我认为这几乎涵盖了所有内容,但是我很乐意通过回答可能存在的任何问题来对此进行补充。
val f = m
编译器的扩展时,val f = new AnyRef with Function1[List[Int], AnyRef] { def apply(x$1: List[Int]) = this.m(x$1) }
您应该指出this
该apply
方法内部未引用AnyRef
对象,而是方法val f = m _
进行评估的对象(外部 this
,即, ),因为this
它是闭包捕获的值之一(例如,return
如下面所指出的)。
方法和功能之间的一个巨大的实际差异是什么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
for (a <- List(1, 2, 3)) { return ... }
怎么办?这已成为现实。
return
从该函数返回一个值,和某种形式的escape
或break
或continue
从方法返回。
函数可以使用参数列表调用函数以产生结果。函数具有参数列表,主体和结果类型。属于类,特征或单例对象的成员的函数称为方法。在其他函数中定义的函数称为局部函数。结果类型为Unit的函数称为过程。源代码中的匿名函数称为函数文字。在运行时,函数文字被实例化为称为函数值的对象。
假设您有清单
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>
功能参数是可选的
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函数比较diff的其他示例,将函数用作变量,创建返回函数的函数
这里有一篇不错的文章,从中可以得到我大多数的描述。关于我的理解,仅是功能和方法的简短比较。希望能帮助到你:
功能:它们基本上是一个对象。更确切地说,函数是具有apply方法的对象;因此,由于它们的开销,它们比方法要慢一些。从某种意义上说,它与静态方法类似,它们独立于要调用的对象。一个简单的函数示例就像下面这样:
val f1 = (x: Int) => x + x
f1(2) // 4
除了将一个对象分配给另一个对象(如object1 = object2)外,上面的行什么都没有。实际上,在我们的示例中,object2是一个匿名函数,因此,左侧获取了对象的类型。因此,现在f1是一个对象(函数)。匿名函数实际上是Function1 [Int,Int]的一个实例,这意味着该函数具有1个类型为Int的参数和返回值为Int类型的函数。不带参数调用f1将为我们提供匿名函数的签名(Int => Int =)
方法:它们不是对象,而是分配给类的实例(即对象)。与Java中的方法或c ++中的成员函数(如Raffi Khatchadourian在对此问题的评论中指出)等完全相同。方法的一个简单示例就像下面这样:
def m1(x: Int) = x + x
m1(2) // 4
上面的行不是简单的值分配,而是方法的定义。当您像第二行一样使用值2调用此方法时,x将替换为2,并且将计算结果,并获得4作为输出。如果只是简单地写入m1,这是一个错误,因为它是方法并且需要输入值。通过使用_,您可以将方法分配给以下函数:
val f2 = m1 _ // Int => Int = <function1>
这是Rob Norris 的精彩文章,解释了区别,这是TL; DR
Scala中的方法不是值,但是函数是。您可以构造一个函数,该函数通过η-expansion(由结尾的下划线thingy触发)委派给方法。
具有以下定义:
一个方法是定义什么DEF和值是你可以分配给VAL
简而言之(摘自博客):
定义方法时,我们看到无法将其分配给val
。
scala> def add1(n: Int): Int = n + 1
add1: (n: Int)Int
scala> val f = add1
<console>:8: error: missing arguments for method add1;
follow this method with `_' if you want to treat it as a partially applied function
val f = add1
还要注意的类型的add1
,看起来并不正常; 您不能声明类型的变量(n: Int)Int
。方法不是价值。
但是,通过添加η-扩展后缀运算符(η表示为“ eta”),我们可以将方法转换为函数值。注意的类型f
。
scala> val f = add1 _
f: Int => Int = <function1>
scala> f(3)
res0: Int = 4
的作用_
是执行与以下操作等效的操作:我们构造了一个Function1
委托给我们方法的实例。
scala> val g = new Function1[Int, Int] { def apply(n: Int): Int = add1(n) }
g: Int => Int = <function1>
scala> g(3)
res18: Int = 4
在Scala 2.13中,与函数不同,方法可以采用/返回
但是,多态函数类型#4672在dotty(Scala 3)中解除了这些限制,例如,dotty版本0.23.0-RC1启用以下语法
类型参数
def fmet[T](x: List[T]) = x.map(e => (e, e))
val ffun = [T] => (x: List[T]) => x.map(e => (e, e))
隐式参数(上下文参数)
def gmet[T](implicit num: Numeric[T]): T = num.zero
val gfun: [T] => Numeric[T] ?=> T = [T] => (using num: Numeric[T]) => num.zero
依赖类型
class A { class B }
def hmet(a: A): a.B = new a.B
val hfun: (a: A) => a.B = hmet