Scala中方法和函数之间的区别

Ana*_*ran 244 methods scala function

我阅读了Scala Functions(另一个Scala之旅的一部分).在那篇文章中他说:

方法和功能不是一回事

但他没有解释任何有关它的事情.他想说什么?

Dan*_*ral 229

吉姆在他的博客文章中已经详细介绍了这一点,但我在这里发布了一个简报以供参考.

首先,让我们看看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除了它的主体之外的一切.

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

函数声明 是一个def声明,包括类型身体.类型部分是方法类型,主体是表达式或块.这也是在Java上用Java调用"方法"的JVM上实现的.

最后,匿名函数函数类型的实例(即特征的实例FunctionN),而方法值是同一个东西!的区别在于,一个方法数值是从方法,或者通过加上后缀下划线创建(m _对应于该"函数声明"的方法的值(def)m),或者通过被称为过程ETA-膨胀,这就像是从方法的自动浇注发挥作用.

这就是规范所说的,所以让我把它放在前面:我们不使用那个术语!这导致了所谓的"函数声明"之间的混淆,它是程序的一部分(第4章 - 基本声明)和"匿名函数",它是一个表达式,而"函数类型",它是,一种类型 - 一种特质.

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

因此,鉴于上述术语的变化,这里是对这种区别的实际解释.

函数是一个对象,包括所述的一个FunctionX特征,诸如Function0,Function1,Function2等.它可以被包括PartialFunction为好,这实际上延伸Function1.

让我们看看其中一个特征的类型签名:

trait Function2[-T1, -T2, +R] extends AnyRef
Run Code Online (Sandbox Code Playgroud)

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

def apply(v1: T1, v2: T2): R
Run Code Online (Sandbox Code Playgroud)

这告诉我们所有人都应该了解它.甲函数有一个apply接收方法Ñ类型的参数T1,T2,...,TN,并返回型的东西R.它在接收的参数上是反变量的,并且在结果上是共变量.

这种差异意味着a Function1[Seq[T], String]是a的子类型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))
Run Code Online (Sandbox Code Playgroud)

这些调用实际上是不同的,因为第一个只是一个语法糖.Scala将其扩展为:

val o1 = f.apply(List(1, 2, 3))
Run Code Online (Sandbox Code Playgroud)

当然,这是对对象的方法调用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"
}
Run Code Online (Sandbox Code Playgroud)

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

val f = m _
Run Code Online (Sandbox Code Playgroud)

假设类型为(Scala 2.7),Scala将扩展:m(List[Int])AnyRef

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

在Scala 2.8上,它实际上使用了一个AbstractFunction1类来减少类的大小.

请注意,无法转换其他方式 - 从函数到方法.

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

def m[T](l: List[T]): String = l mkString ""
Run Code Online (Sandbox Code Playgroud)

我认为这几乎涵盖了所有内容,但我很乐意将其与可能存在的任何问题的答案相辅相成.

  • 这个解释很清楚.做得好.不幸的是,Odersky/Venners/Spoon书和Scala规范都使用了"功能"和"方法"这两个词,可以互换.(他们最有可能说"功能",其中"方法"会更清晰,但有时它也会以相反的方式发生,例如,规范的第6.7节,其中包括将方法转换为函数,被命名为"方法值".我认为,当人们试图学习这门语言时,对这些词语的松散使用会导致很多混乱. (25认同)
  • @tldr [Scala编程](http://www.artima.com/shop/programming_in_scala_2ed),由Odersky等人提供.这是它的常见缩写(他们确实告诉我他们出于某种原因不太喜欢PiS!:) (5认同)
  • @Seth我知道,我知道--PinS是教我斯卡拉的书.我学到了更好的方法,即保罗让我直截了当. (4认同)
  • 很棒的解释!我有一件事要补充:当你引用编译器`val f = m`的扩展为`val f = new AnyRef with Function1 [List [Int],AnyRef] {def apply(x $ 1:List [Int] )= this.m(x $ 1)}`你应该指出`apply`方法中的`this`不是指`AnyRef`对象,而是指``val f = m _的对象`被评估(_outer_`the`,所以说),因为`this`是闭包捕获的值之一(例如,如下面指出的`return`). (4认同)
  • 如果你在 SO 答案中写一篇文章,那么请在它的顶部添加 TL;DR。 (2认同)

Ben*_*ngs 66

方法和功能之间的一个重要的实际区别是什么return意思. return只从方法返回.例如:

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

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

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

scala> f
res4: String = test
Run Code Online (Sandbox Code Playgroud)

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

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

scala> f2
res5: String = is this
Run Code Online (Sandbox Code Playgroud)

  • 那是因为闭包捕获了回报. (9认同)
  • 我想不出一次我想要从函数"返回"到非本地范围.事实上,我可以看到,如果一个函数可以决定它想要更远的堆栈,那么这是一个严重的安全问题.感觉有点像longjmp,只有这样才容易出错.我注意到scalac不会让我从函数返回.这是否意味着这种憎恶已经从语言中被打破了? (4认同)
  • @root - 怎么样从`for( - <List(1,2,3)){return ...}`里面返回?这会被关闭. (2认同)
  • 老实说我会使用不同的语法。让“return”从函数返回一个值,以及某种形式的“escape”或“break”或“continue”从方法返回。 (2认同)

jam*_*het 36

function可以使用参数列表调用函数以生成结果.函数具有参数列表,正文和结果类型.作为类,特征或单个对象的成员的函数称为方法.在其他函数内定义的函数称为局部函数.结果类型为Unit的函数称为过程.源代码中的匿名函数称为函数文字.在运行时,函数文字被实例化为称为函数值的对象.

在Scala第二版中编程.Martin Odersky - Lex Spoon - Bill Venners


ani*_*ish 29

假设你有一个清单

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

定义方法

scala> def m1(i:Int)=i+2
m1: (i: Int)Int
Run Code Online (Sandbox Code Playgroud)

定义一个函数

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)
Run Code Online (Sandbox Code Playgroud)

接受论证的方法

scala> m1(2)
res3: Int = 4
Run Code Online (Sandbox Code Playgroud)

用val定义函数

scala> val p =(i:Int)=>i+2
p: Int => Int = <function1>
Run Code Online (Sandbox Code Playgroud)

功能参数是可选的

 scala> p(2)
    res4: Int = 4

scala> p
res5: Int => Int = <function1>
Run Code Online (Sandbox Code Playgroud)

对方法的争论是强制性的

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
Run Code Online (Sandbox Code Playgroud)

检查以下教程,解释通过其他示例传递其他差异,例如使用方法Vs函数的diff的其他示例,使用函数作为变量,创建返回函数的函数


ept*_*ptx 12

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

  • 这有什么理由吗? (5认同)

Meh*_*ran 6

这里有一篇不错的文章从中可以得到我大多数的描述。关于我的理解,仅是功能和方法的简短比较。希望能帮助到你:

功能:它们基本上是一个对象。更准确地说,函数是具有apply方法的对象;因此,由于它们的开销,它们比方法要慢一些。从某种意义上说,它与静态方法类似,它们独立于要调用的对象。一个简单的函数示例就像下面这样:

val f1 = (x: Int) => x + x
f1(2)  // 4
Run Code Online (Sandbox Code Playgroud)

除了将一个对象分配给另一个对象(如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
Run Code Online (Sandbox Code Playgroud)

上面的行不是简单的值分配,而是方法的定义。当您像第二行一样使用值2调用此方法时,x将替换为2,并且将计算结果,并获得4作为输出。如果只是简单地写入m1,这是一个错误,因为它是方法并且需要输入值。通过使用_,您可以将方法分配给以下函数:

val f2 = m1 _  // Int => Int = <function1>
Run Code Online (Sandbox Code Playgroud)