在Scala中定义函数的这三种方法之间的差异

在Scala中定义函数的这三种方法之间的差异,scala,Scala,给出了表示相同函数的三种方法f(a):=a+1: val f1 = (a:Int) => a + 1 def f2 = (a:Int) => a + 1 def f3:(Int => Int) = a => a + 1 这些定义有何不同?REPL未显示任何明显差异: scala> f1 res38: (Int) => Int = <function1> scala> f2 res39: (Int) => Int = <funct

给出了表示相同函数的三种方法
f(a):=a+1

val f1 = (a:Int) => a + 1
def f2 = (a:Int) => a + 1
def f3:(Int => Int) = a => a + 1
这些定义有何不同?REPL未显示任何明显差异:

scala> f1
res38: (Int) => Int = <function1>
scala> f2
res39: (Int) => Int = <function1>
scala> f3
res40: (Int) => Int = <function1>
scala>f1
res38:(Int)=>Int=
scala>f2
res39:(Int)=>Int=
scala>f3
res40:(Int)=>Int=

f1
是一个接受整数并返回整数的函数

f2
是一种具有零arity的方法,它返回一个接受整数并返回整数的函数。(稍后在REPL中键入
f2
时,它将成为对方法
f2
的调用)


f3
f2
相同。在类中,
val
在初始化时进行计算,而
def
仅在每次调用函数时进行计算。在下面的代码中,您将看到在第一次使用对象时计算x,但在访问x成员时不会再次计算x。相反,在实例化对象时不计算y,而是在每次访问成员时计算y

  class A(a: Int) {
    val x = { println("x is set to something"); a }
    def y = { println("y is set to something"); a }
  }

  // Prints: x is set to something
  val a = new A(1)

  // Prints: "1"
  println(a.x)

  // Prints: "1"                               
  println(a.x)

  // Prints: "y is set to something" and "1"                                  
  println(a.y)

  // Prints: "y is set to something" and "1"                                                                                   
  println(a.y)

执行定义(如defx=e)将不会计算表达式e。相反,每当使用x时,都会计算e。或者,Scala提供了一个值定义 valx=e,作为评估的一部分,它会评估右侧e 定义的定义。如果随后使用x,则立即用 预先计算的值e,因此表达式无需再次求值

Scala示例 作者:马丁·奥德斯基


您应该注意,在上面的第2个块中,在REPL中计算
f1
显示静态绑定到
f1
的值,同时计算
f2
f3
显示调用这些方法的结果。特别是,每次调用
f2
f3
时都会生成一个新的
Function1[Int,Int]
实例,而
f1
永远是相同的
Function1[Int,Int]
。@RandallSchulz,因为val版本不需要新的函数实例,为什么会在这种情况下使用def?@virtualeyes我能回忆起的唯一一种情况是,在combinator解析器库中,可以看到def生成函数n[…]值。编写生成函数的方法并不常见,而且几乎不会使用def生成语义/功能不变函数的多个副本。为什么
f1
函数
f2
方法
?@Freewind,函数是名为
apply
的对象。一种方法,好吧,就是一种方法。真棒的答案。问:你说f2的算术为零,但它不是一元数吗?“空函数不带参数。一元函数带一个参数。”真奇怪@马修康奈尔,
f2
本身不接受任何参数。它返回的函数对象确实如此。@JacobusR只有在类中才是真的吗?例如:scala>varb=5b:Int=5scala>vala:(Int=>Int)=x=>x+baa:Int=>Int=scala>a(5)res48:Int=10scala>b=6b:Int=6scala>a(5)res49:Int=11我期待的是a(5)返回10,b的值已被修改inlined@AndrewCassidy函数
a
是不可变的,并在初始化时进行评估,但
b
仍然是可变值。因此,对
b
的引用在初始化过程中设置,但
b
存储的值保持可变。为了好玩,您现在可以创建一个新的
val b=123
。在此之后,
a(5)
将始终给出11,因为
b
现在是一个全新的值。@JacobusR谢谢。。。这是有道理的。这与“词法范围”的定义一致,因为函数a引用了原始的“var b”。我想让我困惑的是:VarB=5;val c=b;b=6;行为不同。我想我不应该期望一个包含对原始“词法”范围的引用的函数定义与Int的行为方式相同。