在Scala中,函数和方法的区别

19 浏览
0 Comments

在Scala中,函数和方法的区别

我阅读了《Scala函数》(Scala之旅的一部分)。在那篇文章中,他提到:

方法和函数不是相同的东西。

但他没有对此进行任何解释。他想要表达什么意思?

0
0 Comments

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

方法类型是一种非值类型。这意味着没有值——没有对象,没有实例——具有方法类型。正如前面提到的,方法值实际上具有函数类型。方法类型是一个def声明,它包括了除了方法体之外的所有内容。

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

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

最后,匿名函数是函数类型的实例(即,FunctionN特质的实例),方法值也是同样的东西!区别在于方法值是从方法中创建的,可以通过在方法后面加下划线(m _是一个对应于“函数声明”(def)m的方法值),或者通过一种称为eta扩展的过程进行,它类似于从方法到函数的自动转换。

这就是规范所说的,所以让我来解释一下:我们不使用那个术语!它会导致所谓的“函数声明”(程序的一部分,第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的N个参数,并返回类型为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的类型是(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 ""

我认为这基本上涵盖了所有内容,但如果还有任何问题,我很乐意补充回答。

0
0 Comments

在Scala中,方法和函数之间的一个重要区别是关键字return的含义。在方法中,return只会返回方法本身。例如:

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

而在方法中定义的函数中,return会进行非局部返回:

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

而在局部方法中的return仅会返回该方法本身。

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

这是因为闭包捕获了return的值。

我无法想象有任何时候我会想要从函数中进行非局部范围的return。实际上,如果一个函数可以决定它想要跳过更高层的代码,这可能导致严重的安全问题。感觉有点像longjmp,只是更容易出错。不过,我注意到scalac不允许我从函数中进行return。这是否意味着这种滥用已经从语言中删除了呢?

- 那么从for (a <- List(1, 2, 3)) { return ... }中返回呢?这将被转换为一个闭包。

嗯...好吧,这是一个合理的用例。但是仍然有可能导致可怕的难以调试的问题,但是这将其放在了更合理的上下文中。

当你需要“短路”一个折叠操作时,非局部返回也是更好的方法之一,例如:stackoverflow.com/a/12897950/154770

老实说,我会使用不同的语法。使用return从函数中返回一个值,使用某种形式的escapebreakcontinue从方法中返回。

0
0 Comments

在Scala中,函数和方法是编程中常见的两个概念。函数是一段可以通过传入一组参数产生结果的代码。函数拥有参数列表、函数体和返回值类型。而作为类、特质或单例对象的成员的函数则被称为方法。另外,定义在其他函数内部的函数被称为局部函数,而返回类型为Unit的函数被称为过程。在源代码中,匿名函数被称为函数字面量。在运行时,函数字面量会被实例化为称为函数值的对象。

在Scala中,函数和方法虽然有相似之处,但是它们在定义和使用上还是有一些区别的。具体而言,函数可以作为类的成员,通过def关键字来定义。而方法则是类的成员,通过val或var关键字来定义。值得注意的是,只有def定义的函数才能算作方法,val或var定义的函数则只能算作函数。

这种函数和方法的区别在Scala中的定义和使用上都是有意义的。通过将函数定义为方法,可以使其成为类的一部分,从而方便对该类的实例进行调用。而函数则可以作为独立的代码块存在,可以被传递给其他函数或保存为变量。这种灵活性使得函数可以更好地应对不同的编程需求。

总结起来,Scala中的函数和方法虽然有一些区别,但它们都是编程中常用的代码块,可以根据具体的需求选择使用。方法更适合作为类的一部分,而函数则更适合作为独立的代码块存在。通过合理地使用函数和方法,可以使代码更加灵活和可复用。

参考资料:

Programming in Scala Second Edition. Martin Odersky - Lex Spoon - Bill Venners

0