Unterschiede zwischen diesen drei Arten der Definition einer Funktion in Scala

92

Bei drei Möglichkeiten, dieselbe Funktion auszudrücken f(a) := a + 1:

val f1 = (a:Int) => a + 1
def f2 = (a:Int) => a + 1
def f3:(Int => Int) = a => a + 1

Wie unterscheiden sich diese Definitionen? Die REPL weist keine offensichtlichen Unterschiede auf:

scala> f1
res38: (Int) => Int = <function1>
scala> f2
res39: (Int) => Int = <function1>
scala> f3
res40: (Int) => Int = <function1>
qrest
quelle
11
Sie sollten beachten, dass im obigen zweiten Block die Auswertung f1in der REPL den Wert anzeigt, an den sie f1während der Auswertung statisch gebunden ist, f2und f3das Ergebnis des Aufrufs dieser Methoden anzeigt . Insbesondere Function1[Int, Int]wird jedes Mal eine neue Instanz erstellt f2oder f3aufgerufen, während sie für immer f1dieselbe ist Function1[Int, Int].
Randall Schulz
@RandallSchulz Da die val-Version keine neue Funktionsinstanz erfordert, warum sollte man in diesem Fall jemals def verwenden?
virtualeyes
2
@virtualeyes Die einzige Situation, an die ich mich erinnern kann, in der Defs FunctionN [...] -Werte liefern, befindet sich in der Kombinator-Parser-Bibliothek. Es ist nicht sehr üblich, Methoden zu schreiben, die Funktionen liefern, und praktisch nie würde man eine def verwenden, um viele Kopien einer semantisch / funktional unveränderlichen Funktion zu erhalten.
Randall Schulz

Antworten:

112

f1 ist eine Funktion, die eine Ganzzahl akzeptiert und eine Ganzzahl zurückgibt.

f2ist eine Methode mit einer Arität von Null, die eine Funktion zurückgibt, die eine Ganzzahl und eine Ganzzahl zurückgibt. (Wenn Sie f2später bei REPL eingeben, wird die Methode aufgerufen f2.)

f3ist das gleiche wie f2. Sie verwenden dort einfach keine Typinferenz.

fehlender Faktor
quelle
6
Warum f1ist ein functionund f2ist ein method?
Freilauf
17
@Freewind, eine Funktion ist ein Objekt mit einer Methode namens apply. Eine Methode ist eine Methode.
fehlender Faktor
Tolle Antwort. Frage: Sie sagen, f2 hat keine Arität, aber ist es nicht unär? en.wikipedia.org/wiki/Arity "Eine Nullfunktion benötigt keine Argumente. Eine Unärfunktion benötigt ein Argument." Nur neugierig!
Matthew Cornell
5
@MatthewCornell, f2selbst akzeptiert keine Argumente. Das zurückgegebene Funktionsobjekt funktioniert.
fehlender Faktor
122

Innerhalb einer Klasse valwird bei der Initialisierung defausgewertet, während sie nur ausgewertet wird, wenn und jedes Mal die Funktion aufgerufen wird. Im folgenden Code sehen Sie, dass x bei der ersten Verwendung des Objekts ausgewertet wird, jedoch nicht erneut, wenn auf das x-Element zugegriffen wird. Im Gegensatz dazu wird y nicht ausgewertet, wenn das Objekt instanziiert wird, sondern jedes Mal, wenn auf das Mitglied zugegriffen wird.

  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)
Jack
quelle
@JacobusR ist das nur innerhalb einer Klasse wahr?
Andrew Cassidy
Zum Beispiel: 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 Ich hatte erwartet, dass a (5) 10 zurückgibt und der Wert von b inline ist
Andrew Cassidy
@ AndrewCassidy Die Funktion aist unveränderlich und wird bei der Initialisierung ausgewertet, bbleibt jedoch ein veränderbarer Wert. Der Verweis auf bwird also während der Initialisierung gesetzt, aber der von gespeicherte Wert bbleibt veränderbar. Zum Spaß können Sie jetzt eine neue erstellen val b = 123. Danach geben Sie a(5)immer 11, da dies bnun ein völlig neuer Wert ist.
Jack
@ JacobusR danke ... das macht Sinn. Dies stimmt mit der Definition des "lexikalischen Umfangs" überein, da die Funktion a einen Verweis auf das ursprüngliche "var b" enthält. Ich denke, was mich verwirrt hat, ist das Sprichwort: var b = 5; val c = b; b = 6; verhält sich anders. Ich denke, ich sollte nicht erwarten, dass sich eine Funktionsdefinition, die Verweise auf den ursprünglichen "lexikalischen" Bereich enthält, genauso verhält wie ein Int.
Andrew Cassidy
3

Durch Ausführen einer Definition wie def x = e wird der Ausdruck e nicht ausgewertet . Stattdessen wird e immer dann ausgewertet, wenn x verwendet wird. Alternativ bietet Scala eine Wertedefinition val x = e an , die das rechte e als Teil der Bewertung der Definition bewertet. Wenn x anschließend verwendet wird, wird es sofort durch den vorberechneten Wert von e ersetzt , sodass der Ausdruck nicht erneut ausgewertet werden muss.

Scala Mit gutem Beispiel von Martin Odersky

Alexander
quelle