我读过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,这是标准库中trait FunctionN的缩写。匿名函数和方法值具有函数类型,并且函数类型可以用作值、变量和函数声明和定义的一部分。事实上,它可以是方法类型的一部分。

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

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

函数声明是一个def声明,包括类型和主体。类型部分是方法类型,主体是表达式或块。这也是在JVM上实现的,Java称之为“方法”。

最后,一个匿名函数是一个函数类型的实例(例如,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方法,它接收N个类型为T1, T2,…, TN,并返回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))

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

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 type为(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 ""

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

其他回答

实际上,Scala程序员只需要知道以下三条规则就可以正确使用函数和方法:

由def定义的方法和由=>定义的函数字面量是函数。在《Programming in Scala》第4版第8章第143页中定义。 函数值是可以作为任何值传递的对象。函数字面量和部分应用的函数是函数值。 如果在代码中的某个位置需要函数值,则可以省略部分应用的函数的下划线。例如:someNumber.foreach(println)

在《Scala编程》发行了四个版本之后,区分函数和函数值这两个重要概念仍然是个问题,因为所有版本都没有给出明确的解释。语言规范太复杂了。我发现上面的规则既简单又准确。

方法和函数之间一个很大的实际区别是返回的含义。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

这里有一篇不错的文章,我的大部分描述都来自于它。 关于我的理解,只是一个简短的函数和方法的比较。希望能有所帮助:

功能: 它们基本上是一个物体。更准确地说,函数是具有apply方法的对象;因此,由于开销,它们比方法要慢一些。它类似于静态方法,因为它们独立于要调用的对象。 一个简单的函数示例如下所示:

val f1 = (x: Int) => x + x
f1(2)  // 4

The line above is nothing except assigning one object to another like object1 = object2. Actually the object2 in our example is an anonymous function and the left side gets the type of an object because of that. Therefore, now f1 is an object(Function). The anonymous function is actually an instance of Function1[Int, Int] that means a function with 1 parameter of type Int and return value of type Int. Calling f1 without the arguments will give us the signature of the anonymous function (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>

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

首先,让我们看看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,这是标准库中trait FunctionN的缩写。匿名函数和方法值具有函数类型,并且函数类型可以用作值、变量和函数声明和定义的一部分。事实上,它可以是方法类型的一部分。

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

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

函数声明是一个def声明,包括类型和主体。类型部分是方法类型,主体是表达式或块。这也是在JVM上实现的,Java称之为“方法”。

最后,一个匿名函数是一个函数类型的实例(例如,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方法,它接收N个类型为T1, T2,…, TN,并返回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))

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

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 type为(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 ""

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

假设你有一个列表

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

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