Was ist der Unterschied zwischen:
def even: Int => Boolean = _ % 2 == 0
und
val even: Int => Boolean = _ % 2 == 0
Beide können wie genannt werden even(10)
.
Was ist der Unterschied zwischen:
def even: Int => Boolean = _ % 2 == 0
und
val even: Int => Boolean = _ % 2 == 0
Beide können wie genannt werden even(10)
.
Antworten:
Die Methode wird def even
beim Aufruf ausgewertet und erstellt jedes Mal eine neue Funktion (neue Instanz von Function1
).
def even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = false
val even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = true
Mit können def
Sie bei jedem Anruf eine neue Funktion erhalten:
val test: () => Int = {
val r = util.Random.nextInt
() => r
}
test()
// Int = -1049057402
test()
// Int = -1049057402 - same result
def test: () => Int = {
val r = util.Random.nextInt
() => r
}
test()
// Int = -240885810
test()
// Int = -1002157461 - new result
val
wertet aus, wenn definiert, def
- wenn aufgerufen:
scala> val even: Int => Boolean = ???
scala.NotImplementedError: an implementation is missing
scala> def even: Int => Boolean = ???
even: Int => Boolean
scala> even
scala.NotImplementedError: an implementation is missing
Beachten Sie, dass es eine dritte Option gibt : lazy val
.
Es wird beim ersten Aufruf ausgewertet:
scala> lazy val even: Int => Boolean = ???
even: Int => Boolean = <lazy>
scala> even
scala.NotImplementedError: an implementation is missing
Gibt aber FunctionN
jedes Mal das gleiche Ergebnis (in diesem Fall die gleiche Instanz von ) zurück:
lazy val even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = true
lazy val test: () => Int = {
val r = util.Random.nextInt
() => r
}
test()
// Int = -1068569869
test()
// Int = -1068569869 - same result
Performance
val
wird ausgewertet, wenn definiert.
def
Wird bei jedem Anruf ausgewertet, sodass die Leistung möglicherweise schlechter ist als val
bei mehreren Anrufen. Mit einem einzigen Anruf erhalten Sie die gleiche Leistung. Und ohne Anrufe erhalten Sie keinen Overhead def
, sodass Sie ihn definieren können, auch wenn Sie ihn in einigen Filialen nicht verwenden.
Mit a erhalten lazy val
Sie eine verzögerte Bewertung: Sie können sie definieren, auch wenn Sie sie in einigen Zweigen nicht verwenden, und sie wird einmal oder nie ausgewertet, aber Sie erhalten einen kleinen Overhead, wenn Sie bei jedem Zugriff auf Ihre Funktion die doppelte Überprüfung sperren lazy val
.
Wie @SargeBorsch feststellte, können Sie eine Methode definieren, und dies ist die schnellste Option:
def even(i: Int): Boolean = i % 2 == 0
Wenn Sie jedoch eine Funktion (keine Methode) für die Funktionszusammensetzung oder für Funktionen höherer Ordnung (wie filter(even)
) benötigen, generiert der Compiler jedes Mal eine Funktion aus Ihrer Methode, wenn Sie sie als Funktion verwenden, sodass die Leistung möglicherweise etwas schlechter ist als bei val
.
even
Aufruf zu bewerten?
def
kann verwendet werden, um eine Methode zu definieren, und dies ist die schnellste Option. @ A.Karimi
even eq even
.
@inline
Attribut . Funktionen können jedoch nicht inline geschaltet werden, da der Funktionsaufruf ein Aufruf der virtuellen apply
Methode eines Funktionsobjekts ist. JVM kann solche Anrufe in einigen Situationen devirtualisieren und einbinden, jedoch nicht im Allgemeinen.
Bedenken Sie:
scala> def even: (Int => Boolean) = {
println("def");
(x => x % 2 == 0)
}
even: Int => Boolean
scala> val even2: (Int => Boolean) = {
println("val");
(x => x % 2 == 0)
}
val //gets printed while declaration. line-4
even2: Int => Boolean = <function1>
scala> even(1)
def
res9: Boolean = false
scala> even2(1)
res10: Boolean = false
Sehen Sie den Unterschied? Zusamenfassend:
def : Bei jedem Aufruf von even
wird der Hauptteil der even
Methode erneut aufgerufen . Bei even2
ie val wird die Funktion jedoch nur einmal während der Deklaration initialisiert (und wird daher val
in Zeile 4 und nie wieder gedruckt ), und bei jedem Zugriff wird dieselbe Ausgabe verwendet. Versuchen Sie zum Beispiel Folgendes:
scala> import scala.util.Random
import scala.util.Random
scala> val x = { Random.nextInt }
x: Int = -1307706866
scala> x
res0: Int = -1307706866
scala> x
res1: Int = -1307706866
Bei der x
Initialisierung wird der von zurückgegebene Wert Random.nextInt
als Endwert von festgelegt x
. Wenn das nächste Mal x
erneut verwendet wird, wird immer der gleiche Wert zurückgegeben.
Sie können auch träge initialisieren x
. dh bei der ersten Verwendung wird es initialisiert und nicht während der Deklaration. Beispielsweise:
scala> lazy val y = { Random.nextInt }
y: Int = <lazy>
scala> y
res4: Int = 323930673
scala> y
res5: Int = 323930673
even2
zweimal anzurufen , einmal mit 1
und einmal mit 2
. Sie erhalten bei jedem Anruf unterschiedliche Antworten. Während das println
in nachfolgenden Aufrufen nicht ausgeführt wird, erhalten Sie nicht das gleiche Ergebnis aus verschiedenen Aufrufen von even2
. Warum das println
nicht noch einmal ausgeführt wird, ist eine andere Frage.
Sieh dir das an:
var x = 2 // using var as I need to change it to 3 later
val sq = x*x // evaluates right now
x = 3 // no effect! sq is already evaluated
println(sq)
Überraschenderweise wird dies 4 und nicht 9 drucken! val (auch var) wird sofort ausgewertet und zugewiesen.
Ändern Sie nun val in def .. es wird 9 gedruckt! Def ist ein Funktionsaufruf. Er wird bei jedem Aufruf ausgewertet.
val dh "sq" ist per Scala definiert. Es wird direkt zum Zeitpunkt der Deklaration ausgewertet, Sie können es später nicht mehr ändern. In anderen Beispielen, in denen auch2 ebenfalls val ist, aber mit der Funktionssignatur deklariert wurde, dh "(Int => Boolean)", ist es also kein Int-Typ. Es ist eine Funktion und ihr Wert wird durch folgenden Ausdruck festgelegt
{
println("val");
(x => x % 2 == 0)
}
Gemäß der Scala val-Eigenschaft können Sie Even2 keine andere Funktion zuweisen, dieselbe Regel wie sq.
Warum ruft die Funktion eval2 val nicht immer wieder "val" auf?
Ursprungscode:
val even2: (Int => Boolean) = {
println("val");
(x => x % 2 == 0)
}
Wir wissen, dass in Scala die letzte Aussage des obigen Ausdrucks (innerhalb von {..}) tatsächlich auf die linke Seite zurückkehrt. Am Ende setzen Sie Even2 auf die Funktion "x => x% 2 == 0", die mit dem Typ übereinstimmt, den Sie für den Val-Typ "Even2" deklariert haben, dh (Int => Boolean), sodass der Compiler zufrieden ist. Jetzt zeigt sogar2 nur noch auf die Funktion "(x => x% 2 == 0)" (keine andere Anweisung vor dh println ("val") usw. Wenn Sie event2 mit verschiedenen Parametern aufrufen, wird tatsächlich "(x => x% 2" aufgerufen == 0) "Code, da nur dieser mit event2 gespeichert wird.
scala> even2(2)
res7: Boolean = true
scala> even2(3)
res8: Boolean = false
Um dies näher zu verdeutlichen, folgt eine andere Version des Codes.
scala> val even2: (Int => Boolean) = {
| println("val");
| (x => {
| println("inside final fn")
| x % 2 == 0
| })
| }
Was wird passieren ? hier wird immer wieder "inside final fn" gedruckt, wenn Sie Even2 () aufrufen.
scala> even2(3)
inside final fn
res9: Boolean = false
scala> even2(2)
inside final fn
res10: Boolean = true
scala>
Durch Ausführen einer Definition wie def x = e
wird der Ausdruck nicht ausgewertet. E. Stattdessen wird e ausgewertet, wenn x aufgerufen wird.
Alternativ bietet Scala eine Wertedefinition an
val x = e
, die die rechte Seite 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.
Val ist auch eine Bewertung nach Wert. Dies bedeutet, dass der Ausdruck auf der rechten Seite während der Definition ausgewertet wird. Wobei Def durch Namensauswertung ist. Es wird nicht ausgewertet, bis es verwendet wird.
Zusätzlich zu den oben genannten hilfreichen Antworten sind meine Ergebnisse:
def test1: Int => Int = {
x => x
}
--test1: test1[] => Int => Int
def test2(): Int => Int = {
x => x+1
}
--test2: test2[]() => Int => Int
def test3(): Int = 4
--test3: test3[]() => Int
Das Obige zeigt, dass "def" eine Methode (mit Nullargumentparametern) ist, die beim Aufrufen eine andere Funktion "Int => Int" zurückgibt.
Die Konvertierung von Methoden in Funktionen wird hier ausführlich erläutert: https://tpolecat.github.io/2014/06/09/methods-functions.html
In REPL,
scala> def even: Int => Boolean = { _% 2 == 0 }
even: Int => Boolean
scala> val even: Int => Boolean = { _% 2 == 0 }
even: Int => Boolean = $$Lambda$1157/1017502292@57a0aeb8
def bedeutet call-by-name
, auf Anfrage ausgewertet
val bedeutet call-by-value
, während der Initialisierung ausgewertet
Int => Boolean
das? Ich denke, die definierte Syntax istdef foo(bar: Baz): Bin = expr