我阅读scala函数(scala的另一个教程的一部分)。他在那篇文章中说:
Methods and functions are not the same thing
但他什么也没解释。他想说什么?
- 2009年05 jim-mcbeath.blogspot.com / / / & hellip;
- 我想你可以得到什么,从什么是差分方法之间的一个功能
- 一个后续的问题与答案的好方法:函数在Scala VS
吉姆在他的博客中对此进行了大量报道,但我在这里发布了一份简报以供参考。好的。
首先,让我们看看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的缩写。匿名函数和方法值具有函数类型,函数类型可以用作值、变量、函数声明和定义的一部分。实际上,它可以是方法类型的一部分。好的。
方法类型是非值类型。这意味着没有方法类型的值-没有对象,没有实例。如上所述,方法值实际上具有函数类型。方法类型是一个def声明——除了它的主体之外,所有关于def的内容。好的。
值声明和定义以及变量声明和定义是val和var声明,包括类型和值-分别是函数类型和匿名函数或方法值。注意,在JVM上,这些(方法值)是用Java调用的"方法"来实现的。好的。
函数声明是一个def声明,包括类型和主体。类型部分是方法类型,主体是表达式或块。这也在JVM上实现,Java调用了"方法"。好的。
最后,匿名函数是一个函数类型的实例(即特性FunctionN的实例),方法值也是一样的!区别在于,方法值是从方法创建的,要么是通过固定下划线(m _是对应于"函数声明"(defm的方法值),要么是通过名为eta扩展的过程创建的,这类似于从方法到函数的自动转换。好的。
规范就是这么说的,所以让我把这个放在前面:我们不使用这个术语!它导致了所谓的"函数声明"(属于程序的一部分)(第4章——基本声明)和"匿名函数"(表达式)以及"函数类型"(也就是类型——特性)之间的混淆。好的。
下面的术语是由经验丰富的scala程序员使用的,它与规范的术语有一个不同:我们说的不是函数声明,而是方法。甚至方法声明。此外,我们注意到值声明和变量声明也是用于实际目的的方法。好的。
因此,考虑到上述术语的变化,这里有一个实际的区别解释。好的。
函数是一个包含FunctionX特征之一的对象,如Function0、Function1、Function2等,它也可能包含PartialFunction,实际上扩展了Function1。好的。
让我们看看这些特性之一的类型签名:好的。
这个特性有一个抽象的方法(它也有一些具体的方法):好的。
1
| 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是一个作用域局部的方法,那么两者都可以这样调用:好的。
1 2
| val o1 = f (List (1, 2, 3))
val o2 = m (List (1, 2, 3)) |
这些调用实际上是不同的,因为第一个调用只是语法上的糖分。scala将其扩展到:好的。
1
| val o1 = f. apply(List (1, 2, 3)) |
当然,它是对对象f的方法调用。函数还有其他的语法优势:函数文本(实际上是其中两个)和(T1, T2) => R类型的签名。例如:好的。
1 2 3 4 5 6
| val f = (l : List [Int ]) => l mkString ""
val g : (AnyVal ) => String = {
case i : Int =>"Int"
case d : Double =>"Double"
case o =>"Other"
} |
方法和函数之间的另一个相似之处是前者可以很容易地转换为后者:好的。
scala会将其扩展,假设m类型为(List[Int])AnyRef类型为(scala 2.7):好的。
1 2 3
| 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可以参数化它:好的。
1
| def m [T ](l : List [T ]): String = l mkString "" |
我认为这几乎涵盖了所有的事情,但我很乐意用对任何可能存在的问题的答案来补充这一点。好的。好啊。
- 这个解释很清楚。做得好。不幸的是,奥德斯基/Venners/Spoon Book和scala规范都使用了"函数"和"方法"这两个词,这两个词在某种程度上是可以互换的。(最有可能的说法是"函数",其中"方法"更清晰,但有时也会以另一种方式发生,例如,规范第6.7节(涉及将方法转换为函数)被称为"方法值"。啊。)我认为,当人们试图学习语言时,这些词的松散使用导致了很多混乱。
- @赛斯,我知道,我知道--别针是教我斯卡拉的书。我学到了更好的方法.
- 很好的解释!我有一件事要补充:当你引用编译程序val f = new AnyRef with Function1[List[Int], AnyRef] { def apply(x$1: List[Int]) = this.m(x$1) }对val f = m的扩展时,你应该指出,apply方法中的this不是指AnyRef对象,而是指的是对val f = m _方法进行评估的对象(即外部this),因为thisis是由闭包捕获的值中的一个(如下面指出的return)。
- @丹妮尔。索布拉尔,你提到的别针书是什么?我对学习斯卡拉也很感兴趣,还没有找到一本同名的书,
- @tldr在scala中编程,由ordersky et all编写。这是它的常用缩写(他们确实告诉我他们不太喜欢小便,因为某些原因!:)
- @丹妮尔克。索布拉尔,我不确定别针的发音是否快得多!
- 如果你用这样的答案写一篇文章,请在上面加上tl;dr。
一个方法和一个函数之间的一个巨大的实际区别是return的意思。return只从方法返回。例如:
1 2 3 4
| scala > val f = () => { return"test" }
<console >:4: error : return outside method definition
val f = () => { return"test" }
^ |
从方法中定义的函数返回执行非本地返回:
1 2 3 4 5 6 7 8 9
| scala > def f : String = {
| val g = () => { return"test" }
| g ()
| "not this"
| }
f : String
scala > f
res4 : String = test |
而从本地方法返回只从该方法返回。
1 2 3 4 5 6 7 8 9
| scala > def f2 : String = {
| def g (): String = { return"test" }
| g ()
| "is this"
| }
f2 : String
scala > f2
res5 : String = is this |
- 那是因为回报被关闭所捕获。
- 哇,我看到这会导致一些非常混乱的代码…
- 我想不出一次我想从函数"返回"到非本地范围。事实上,如果一个函数能够决定它想在堆栈上再后退一点,那么我认为这是一个严重的安全问题。感觉有点像朗吉姆普,唯一容易意外出错的方法。不过,我注意到scalac不允许我从函数返回。这是否意味着这种可憎的行为已经从语言中消失了?
- @根-从for (a <- List(1, 2, 3)) { return ... }内部返回如何?这会导致糖分的减少。
- 隐马尔可夫模型。。。嗯,这是一个合理的用例。仍然有可能导致非常难以调试的问题,但这确实使它处于一个更明智的环境中。
- 当需要"短路"折叠时,非本地返回也是更好的方法之一,例如:stackoverflow.com/a/12897950/154770
- 老实说,我会使用不同的语法。使return从函数返回值,并使某种形式的escape或break或continue从方法返回。
function A function can be invoked with a list of arguments to produce a
result. A function has a parameter list, a body, and a result type.
Functions that are members of a class, trait, or singleton object are
called methods. Functions defined inside other functions are called
local functions. Functions with the result type of Unit are called procedures.
Anonymous functions in source code are called function literals.
At run time, function literals are instantiated into objects called
function values.
在scala第二版中编程。马丁·奥德斯基-雷克斯·斯彭-比尔·文纳
- 函数可以作为def或val/var属于类。只有def是方法。
假设你有一个清单
1 2
| scala > val x =List. range(10, 20)
x : List [Int ] = List (10, 11, 12, 13, 14, 15, 16, 17, 18, 19) |
定义一种方法
1 2
| scala > def m1 (i :Int )=i+ 2
m1 : (i : Int )Int |
定义函数
1 2 3 4 5
| 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) |
方法接受参数
1 2
| scala> m1(2)
res3: Int = 4 |
用val定义函数
1 2
| scala > val p =(i :Int )=>i+ 2
p : Int => Int = <function1 > |
函数的参数是可选的
1 2 3 4 5
| scala> p(2)
res4: Int = 4
scala> p
res5: Int => Int = <function1> |
方法的参数是必需的
1 2 3
| 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示例,使用函数作为变量,创建返回函数的函数
函数不支持参数默认值。方法可以。从方法转换为函数将丢失参数默认值。(Scala 2.8-1)
这里有一篇很好的文章,我的大部分描述都是从中提取出来的。就我的理解而言,只是简单地比较一下函数和方法。希望它有帮助:
功能:它们基本上是一个物体。更准确地说,函数是具有apply方法的对象;因此,由于它们的开销,它们比方法慢一些。它类似于静态方法,因为它们独立于要调用的对象。函数的一个简单示例如下所示:
1 2
| val f1 = (x : Int ) => x + x
f1 (2) // 4 |
上面的行只是将一个对象分配给另一个对象,比如object1=object2。实际上,我们的示例中的object2是一个匿名函数,因此左侧得到了一个对象的类型。因此,现在f1是一个对象(函数)。匿名函数实际上是function1[int,int]的一个实例,它表示一个参数为int类型,返回值为int类型的函数。在没有参数的情况下调用f1将为我们提供匿名函数的签名(int=>int=)
方法:它们不是对象,而是分配给类的实例,即对象。与Java中的方法完全相同,或者C++中的成员函数(如Raffi Khatchadourian在这个问题的注释中指出的)等。一个简单的方法示例如下:
1 2
| def m1 (x : Int ) = x + x
m1 (2) // 4 |
上面的行不是简单的值赋值,而是方法的定义。当使用值2(如第二行)调用此方法时,x替换为2,计算结果,得到4作为输出。这里,如果只写m1,就会得到一个错误,因为它是方法,需要输入值。通过使用,您可以将一个方法分配给如下函数:
1
| val f2 = m1 _ // Int => Int = <function1> |
- "给函数赋值"是什么意思?这仅仅意味着你现在有了一个和方法行为相同的对象吗?
这是罗布·诺里斯的一篇很好的文章,解释了两者的区别,这里是一篇TL;博士
Methods in Scala are not values, but functions are. You can construct a function that delegates to a method via η-expansion (triggered by the trailing underscore thingy).
定义如下:
a method is something defined with def and a value is something you can assign to a val
简而言之(摘自博客):
当我们定义一个方法时,我们看到我们不能将它赋给val。
1 2 3 4 5 6 7
| 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的类型。
1 2 3 4 5
| scala > val f = add1 _
f : Int => Int = <function1 >
scala > f (3)
res0 : Int = 4 |
_的效果相当于执行以下操作:我们构造了一个Function1实例来委托我们的方法。
1 2 3 4 5
| scala > val g = new Function1 [Int, Int ] { def apply (n : Int ): Int = add1 (n ) }
g : Int => Int = <function1 >
scala > g (3)
res18 : Int = 4 |