Hallo, was heißt Int => Booleandas? Ich denke, die definierte Syntax istdef foo(bar: Baz): Bin = expr
Ziu
@Ziu bedeutet, dass die Funktion 'gerade' ein Int als Argument empfängt und einen Booleschen Wert als Werttyp zurückgibt. Sie können also 'gerade (3)' aufrufen, was zu Booleschem
Wert
@DenysLobur danke für deine Antwort! Irgendwelche Hinweise zu dieser Syntax?
Ziu
@Ziu Ich habe es im Grunde aus Oderskys Coursera-Kurs herausgefunden - coursera.org/learn/progfun1 . Wenn Sie fertig sind, werden Sie verstehen, was 'Typ => Typ' bedeutet
Denys Lobur
Antworten:
325
Die Methode wird def evenbeim Aufruf ausgewertet und erstellt jedes Mal eine neue Funktion (neue Instanz von Function1).
def even:Int=>Boolean= _ %2==0
even eq even
//Boolean = falseval even:Int=>Boolean= _ %2==0
even eq even
//Boolean = true
Mit können defSie bei jedem Anruf eine neue Funktion erhalten:
val test:()=>Int={val r = util.Random.nextInt
()=> r
}
test()// Int = -1049057402
test()// Int = -1049057402 - same resultdef test:()=>Int={val r = util.Random.nextInt
()=> r
}
test()// Int = -240885810
test()// Int = -1002157461 - new result
valwertet 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>lazyval even:Int=>Boolean=???
even:Int=>Boolean=<lazy>
scala> even
scala.NotImplementedError: an implementation is missing
Gibt aber FunctionNjedes Mal das gleiche Ergebnis (in diesem Fall die gleiche Instanz von ) zurück:
lazyval even:Int=>Boolean= _ %2==0
even eq even
//Boolean = truelazyval test:()=>Int={val r = util.Random.nextInt
()=> r
}
test()// Int = -1068569869
test()// Int = -1068569869 - same result
Performance
val wird ausgewertet, wenn definiert.
defWird bei jedem Anruf ausgewertet, sodass die Leistung möglicherweise schlechter ist als valbei 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 valSie 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.
Würden Sie sie bitte hinsichtlich der Leistung vergleichen? Ist es nicht wichtig, die Funktion bei jedem evenAufruf zu bewerten?
Amir Karimi
2
defkann verwendet werden, um eine Methode zu definieren, und dies ist die schnellste Option. @ A.Karimi
Anzeigename
2
Zum Spaß: am 2.12 , even eq even.
Som-Snytt
Gibt es ein Konzept für Inline-Funktionen wie in C ++? Ich komme aus der C ++ - Welt, also verzeihen Sie meine Unwissenheit.
animageofmine
2
@animageofmine Der Scala-Compiler kann versuchen, Methoden zu integrieren. Dafür gibt es ein @inlineAttribut . Funktionen können jedoch nicht inline geschaltet werden, da der Funktionsaufruf ein Aufruf der virtuellen applyMethode eines Funktionsobjekts ist. JVM kann solche Anrufe in einigen Situationen devirtualisieren und einbinden, jedoch nicht im Allgemeinen.
Senia
24
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 evenwird der Hauptteil der evenMethode erneut aufgerufen . Bei even2ie val wird die Funktion jedoch nur einmal während der Deklaration initialisiert (und wird daher valin Zeile 4 und nie wieder gedruckt ), und bei jedem Zugriff wird dieselbe Ausgabe verwendet. Versuchen Sie zum Beispiel Folgendes:
scala>import scala.util.Randomimport scala.util.Random
scala>val x ={Random.nextInt }
x:Int=-1307706866
scala> x
res0:Int=-1307706866
scala> x
res1:Int=-1307706866
Bei der xInitialisierung wird der von zurückgegebene Wert Random.nextIntals Endwert von festgelegt x. Wenn das nächste Mal xerneut 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>lazyval y ={Random.nextInt }
y:Int=<lazy>
scala> y
res4:Int=323930673
scala> y
res5:Int=323930673
Ich denke, Ihre Erklärung könnte etwas implizieren, das Sie nicht beabsichtigen. Versuchen Sie even2zweimal anzurufen , einmal mit 1und einmal mit 2. Sie erhalten bei jedem Anruf unterschiedliche Antworten. Während das printlnin nachfolgenden Aufrufen nicht ausgeführt wird, erhalten Sie nicht das gleiche Ergebnis aus verschiedenen Aufrufen von even2. Warum das printlnnicht noch einmal ausgeführt wird, ist eine andere Frage.
Melston
1
das ist eigentlich sehr interessant. Es ist wie im Fall von val, dh gerade2, der Wert wird zu einem parametrisierten Wert ausgewertet. also ja mit einem val du die auswertung der funktion, deren wert. Der Ausdruck ist nicht Teil des ausgewerteten Wertes. Es ist Teil der Bewertung, aber nicht der bewertete Wert. Der Trick dabei ist, dass der ausgewertete Wert tatsächlich ein parametrisierter Wert ist, der von einer Eingabe abhängt. kluges Ding
MaatDeamon
1
@melston genau! Das habe ich verstanden. Warum wird der Druck nicht erneut ausgeführt, während sich die Ausgabe ändert?
aur
1
@aur Was von Even2 zurückgegeben wird, ist eigentlich eine Funktion (der Ausdruck in Klammern am Ende der Definition von Even2). Diese Funktion wird tatsächlich mit dem Parameter aufgerufen, den Sie bei jedem Aufruf an even2 übergeben.
Melston
5
Sieh dir das an:
var x =2// using var as I need to change it to 3 laterval 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.
Durch Ausführen einer Definition wie def x = ewird 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=>Intdef test2():Int=>Int={
x => x+1}--test2: test2[]()=>Int=>Intdef 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.
Bei einer so alten Frage und bei so vielen bereits eingereichten Antworten ist es oft hilfreich zu erklären, wie sich Ihre Antwort von den Informationen in den vorhandenen Antworten unterscheidet oder diese ergänzt.
Int => Boolean
das? Ich denke, die definierte Syntax istdef foo(bar: Baz): Bin = expr
Antworten:
Die Methode wird
def even
beim Aufruf ausgewertet und erstellt jedes Mal eine neue Funktion (neue Instanz vonFunction1
).Mit können
def
Sie bei jedem Anruf eine neue Funktion erhalten:val
wertet aus, wenn definiert,def
- wenn aufgerufen:Beachten Sie, dass es eine dritte Option gibt :
lazy val
.Es wird beim ersten Aufruf ausgewertet:
Gibt aber
FunctionN
jedes Mal das gleiche Ergebnis (in diesem Fall die gleiche Instanz von ) zurück:Performance
val
wird ausgewertet, wenn definiert.def
Wird bei jedem Anruf ausgewertet, sodass die Leistung möglicherweise schlechter ist alsval
bei mehreren Anrufen. Mit einem einzigen Anruf erhalten Sie die gleiche Leistung. Und ohne Anrufe erhalten Sie keinen Overheaddef
, 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 sperrenlazy val
.Wie @SargeBorsch feststellte, können Sie eine Methode definieren, und dies ist die schnellste Option:
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 beival
.quelle
even
Aufruf zu bewerten?def
kann verwendet werden, um eine Methode zu definieren, und dies ist die schnellste Option. @ A.Karimieven eq even
.@inline
Attribut . Funktionen können jedoch nicht inline geschaltet werden, da der Funktionsaufruf ein Aufruf der virtuellenapply
Methode eines Funktionsobjekts ist. JVM kann solche Anrufe in einigen Situationen devirtualisieren und einbinden, jedoch nicht im Allgemeinen.Bedenken Sie:
Sehen Sie den Unterschied? Zusamenfassend:
def : Bei jedem Aufruf von
even
wird der Hauptteil dereven
Methode erneut aufgerufen . Beieven2
ie val wird die Funktion jedoch nur einmal während der Deklaration initialisiert (und wird daherval
in Zeile 4 und nie wieder gedruckt ), und bei jedem Zugriff wird dieselbe Ausgabe verwendet. Versuchen Sie zum Beispiel Folgendes:Bei der
x
Initialisierung wird der von zurückgegebene WertRandom.nextInt
als Endwert von festgelegtx
. Wenn das nächste Malx
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:quelle
even2
zweimal anzurufen , einmal mit1
und einmal mit2
. Sie erhalten bei jedem Anruf unterschiedliche Antworten. Während dasprintln
in nachfolgenden Aufrufen nicht ausgeführt wird, erhalten Sie nicht das gleiche Ergebnis aus verschiedenen Aufrufen voneven2
. Warum dasprintln
nicht noch einmal ausgeführt wird, ist eine andere Frage.Sieh dir das an:
Ü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.
quelle
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
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:
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.
Um dies näher zu verdeutlichen, folgt eine andere Version des Codes.
Was wird passieren ? hier wird immer wieder "inside final fn" gedruckt, wenn Sie Even2 () aufrufen.
quelle
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.quelle
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.
quelle
Zusätzlich zu den oben genannten hilfreichen Antworten sind meine Ergebnisse:
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
quelle
In REPL,
def bedeutet
call-by-name
, auf Anfrage ausgewertetval bedeutet
call-by-value
, während der Initialisierung ausgewertetquelle