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


92

给出了三种表达相同功能的方式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>

11
您应该注意,在上面的第二个块f1中,REPL中的f1评估显示了评估时静态绑定到的值,f2f3显示了调用这些方法的结果。特别地,一个新的Function1[Int, Int]实例产生每当任一f2f3调用,而f1是相同的Function1[Int, Int]永远。
兰德尔·舒尔茨

@RandallSchulz给出val版本不需要新的函数实例,为什么在这种情况下会使用def?
virtualeyes 2012年

2
@virtualeyes我唯一记得的情况就是组合器解析器库中看到defs产生FunctionN [...]值的情况。编写产生函数的方法不是很常见,并且几乎从不使用def来产生语义/功能不变的函数的许多副本。
Randall Schulz 2012年

Answers:


112

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

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

f3与相同f2。您只是不在那里使用类型推断。


6
为什么f1functionf2method
Freewind

17
@Freewind,函数是具有名为的方法的对象apply。方法就是方法。
missingfaktor 2013年

很棒的答案。问题:您说f2的Arity为零,但不是一元吗?“ en.wikipedia.org/wiki/Arity ”无效函数不带任何参数。一元函数只带一个参数。“ 只是好奇!
马修·康奈尔

5
@MatthewCornell f2本身不接受任何参数。它返回的函数对象可以。
missingfaktor

122

在类内部,val在初始化时def求值,而仅在每次调用该函数时求值。在下面的代码中,您将看到x在第一次使用该对象时被评估,但是在访问x成员时不再评估。相反,在实例化对象时不评估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)

@JacobusR仅在一个类中才是真的吗?
Andrew Cassidy 2014年

例如:scala> var b = 5 b:Int = 5 scala> val a:(Int => Int)= x => x + ba:Int => Int = <function1> scala> a(5)res48:Int = 10 scala> b = 6 b:Int = 6 scala> a(5)res49:Int = 11我期望a(5)返回10,并且内联了b的值
Andrew Cassidy 2014年

@AndrewCassidy该函数a是不变的,并在初始化时进行评估,但b仍是可变值。因此,b在初始化过程中会设置对的引用,但存储的值b仍然可变。为了娱乐,您现在可以创建一个新的val b = 123。在此之后,您a(5)将始终给出11,因为b现在是一个全新的值。
2014年

@JacobusR谢谢...这很有道理。这与“词法范围”的定义相吻合,因为函数a带有对原始“ var b”的引用。我想让我感到困惑的是:var b = 5; val c = b; b = 6;行为有所不同。我猜我不应该期望函数定义带有对原始“词法”作用域的引用,其行为方式与Int相同。
Andrew Cassidy 2014年

3

执行诸如def x = e之类的定义将不对表达式e求值。取而代之的是,每当使用x时就对e求值。另外,Scala提供了一个值定义 val x = e,它确实评估右侧e作为定义评估的一部分。如果随后使用x,则立即将其替换为e的预先计算的值,从而无需再次评估表达式。

Martin Odersky的 Scala范例

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.