我读过Scala函数(Scala另一个指南的一部分)。在那篇帖子中,他说:
方法和函数不是一回事
但他什么也没解释。他到底想说什么?
我读过Scala函数(Scala另一个指南的一部分)。在那篇帖子中,他说:
方法和函数不是一回事
但他什么也没解释。他到底想说什么?
当前回答
这是Rob Norris写的一篇很棒的文章,解释了两者的区别
Scala中的方法不是值,但函数是。您可以构造一个通过η扩展(由后面的下划线触发)委托给某个方法的函数。
定义如下:
方法是用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> 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函数的其他示例,使用函数作为变量,创建返回函数的函数
区别是细微的,但却是实质性的,它与所使用的类型系统有关(除了来自面向对象或函数范式的术语)。
当我们谈论函数时,我们谈论的是函数类型:它是一种类型,它的实例可以作为输入或输出传递给其他函数(至少在Scala中是这样)。
当我们谈论(类的)方法时,我们实际上是在谈论它所属的类所表示的类型:也就是说,方法只是更大类型的一个组件,不能单独传递。它必须与它所属类型的实例(即类的实例)一起传递。
在Scala 2.13中,与函数不同,方法可以接受/返回
类型参数(多态方法) 隐式参数 从属类型
然而,这些限制在dotty (Scala 3)中通过多态函数类型#4672解除了,例如,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
更多示例请参见tests/run/ polymorphism -functions.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
方法属于一个对象(通常是定义它的类、trait或对象),而函数本身是一个值,因为在Scala中每个值都是一个对象,因此,函数是一个对象。
例如,给定下面的方法和函数:
def timesTwoMethod(x :Int): Int = x * 2
def timesTwoFunction = (x: Int) => x * 2
第二个def是Int => Int类型的对象(Function1[Int, Int]的语法糖)。
Scala将函数作为对象,这样它们就可以作为一级实体使用。通过这种方式,可以将函数作为参数传递给其他函数。
然而,Scala也可以通过一种称为Eta展开的机制将方法视为函数。
例如,定义在List上的高阶函数映射,接收另一个函数f: A => B作为其唯一参数。接下来的两行是等价的:
List(1, 2, 3).map(timesTwoMethod)
List(1, 2, 3).map(timesTwoFunction)
当编译器在需要函数的地方看到def时,它会自动将该方法转换为等效的函数。