Ich habe Scala-Funktionen gelesen (Teil einer weiteren Tour durch Scala ). In diesem Beitrag erklärte er:
Methoden und Funktionen sind nicht dasselbe
Aber er hat nichts darüber erklärt. Was wollte er sagen?
Ich habe Scala-Funktionen gelesen (Teil einer weiteren Tour durch Scala ). In diesem Beitrag erklärte er:
Methoden und Funktionen sind nicht dasselbe
Aber er hat nichts darüber erklärt. Was wollte er sagen?
Antworten:
Jim hat dies in seinem Blog-Beitrag ziemlich ausführlich behandelt , aber ich poste hier ein Briefing als Referenz.
Lassen Sie uns zunächst sehen, was uns die Scala-Spezifikation sagt. Kapitel 3 (Typen) enthält Informationen zu Funktionstypen (3.2.9) und Methodentypen (3.3.1). Kapitel 4 (Grunddeklarationen) spricht von Werterklärungen und Definitionen (4.1), Variablendeklarationen und Definitionen (4.2) und Funktionsdeklarationen und Definitionen (4.6). Kapitel 6 (Ausdrücke) spricht von anonymen Funktionen (6.23) und Methodenwerten (6.7). Seltsamerweise wird am 3.2.9 einmal von Funktionswerten gesprochen, und nirgendwo anders.
Ein Funktionstyp ist (ungefähr) ein Typ der Form (T1, ..., Tn) => U , die eine Abkürzung für das Merkmal FunctionN
in der Standardbibliothek ist. Anonyme Funktionen und Methodenwerte haben Funktionstypen, und Funktionstypen können als Teil von Wert-, Variablen- und Funktionsdeklarationen und -definitionen verwendet werden. Tatsächlich kann es Teil eines Methodentyps sein.
Ein Methodentyp ist ein Nichtwerttyp . Das heißt, es gibt keinen Wert - kein Objekt, keine Instanz - mit einem Methodentyp. Wie oben erwähnt, hat ein Methodenwert tatsächlich einen Funktionstyp . Ein Methodentyp ist eine def
Deklaration - alles über a def
außer seinem Körper.
Wertdeklarationen und -definitionen sowie Variablendeklarationen und -definitionen sind val
und var
Deklarationen, einschließlich Typ und Wert , die jeweils Funktionstyp- und anonyme Funktionen oder Methodenwerte sein können . Beachten Sie, dass diese (Methodenwerte) in der JVM mit den von Java als "Methoden" bezeichneten Methoden implementiert werden.
Eine Funktionsdeklaration ist eine def
Deklaration, einschließlich Typ und Text . Der Typteil ist der Methodentyp, und der Körper ist ein Ausdruck oder ein Block . Dies wird auch in der JVM mit den von Java als "Methoden" bezeichneten Methoden implementiert.
Schließlich ist eine anonyme Funktion eine Instanz eines Funktionstyps (dh eine Instanz des Merkmals FunctionN
), und ein Methodenwert ist dasselbe! Der Unterschied besteht darin, dass ein Methodenwert aus Methoden erstellt wird, entweder durch Nachfixieren eines Unterstrichs ( m _
ist ein Methodenwert, der der "Funktionsdeklaration" ( def
) entspricht m
) oder durch einen Prozess namens eta-extension , der einer automatischen Umwandlung von method ähnelt Funktionieren.
Das sagen die Spezifikationen, also lassen Sie mich dies vorwegnehmen: Wir verwenden diese Terminologie nicht! Dies führt zu einer zu großen Verwechslung zwischen der sogenannten "Funktionsdeklaration" , die Teil des Programms ist (Kapitel 4 - Grunddeklarationen), und der "anonymen Funktion" , die ein Ausdruck ist, und dem "Funktionstyp" , dh Nun, ein Typ - eine Eigenschaft.
Die folgende Terminologie, die von erfahrenen Scala-Programmierern verwendet wird, ändert sich gegenüber der Terminologie der Spezifikation: Anstatt Funktionsdeklaration zu sagen, sagen wir Methode . Oder sogar Methodendeklaration. Darüber hinaus stellen wir fest, dass Wertdeklarationen und Variablendeklarationen auch Methoden für praktische Zwecke sind.
Angesichts der obigen Änderung der Terminologie finden Sie hier eine praktische Erklärung der Unterscheidung.
Eine Funktion ist ein Objekt , das eines der umfasst FunctionX
Merkmale, wie zum Beispiel Function0
, Function1
, Function2
usw. Es könnte sein , einschließlich PartialFunction
als auch, was sich eigentlich Function1
.
Sehen wir uns die Typensignatur für eines dieser Merkmale an:
trait Function2[-T1, -T2, +R] extends AnyRef
Dieses Merkmal hat eine abstrakte Methode (es gibt auch einige konkrete Methoden):
def apply(v1: T1, v2: T2): R
Und das sagt uns alles, was es darüber zu wissen gibt. Eine Funktion hat eine apply
Methode, die N Parameter vom Typ T1 , T2 , ..., TN empfängt und etwas vom Typ zurückgibt R
. Es ist eine Gegenvariante der empfangenen Parameter und eine Co-Variante des Ergebnisses.
Diese Varianz bedeutet, dass a Function1[Seq[T], String]
ein Subtyp von ist Function1[List[T], AnyRef]
. Ein Subtyp zu sein bedeutet, dass er anstelle davon verwendet werden kann. Man kann leicht erkennen, f(List(1, 2, 3))
dass einer AnyRef
der beiden oben genannten Typen funktionieren würde , wenn ich anrufen und einen Rücken erwarten würde.
Was ist nun die Ähnlichkeit einer Methode und einer Funktion? Nun, wenn f
es sich um eine Funktion und m
eine lokale Methode im Bereich handelt, können beide folgendermaßen aufgerufen werden:
val o1 = f(List(1, 2, 3))
val o2 = m(List(1, 2, 3))
Diese Aufrufe sind tatsächlich unterschiedlich, da der erste nur ein syntaktischer Zucker ist. Scala erweitert es auf:
val o1 = f.apply(List(1, 2, 3))
Was natürlich ein Methodenaufruf für ein Objekt ist f
. Funktionen haben auch andere syntaktische Zucker im Vorteil: Funktionsliterale (zwei davon tatsächlich) und (T1, T2) => R
Typensignaturen. Beispielsweise:
val f = (l: List[Int]) => l mkString ""
val g: (AnyVal) => String = {
case i: Int => "Int"
case d: Double => "Double"
case o => "Other"
}
Eine weitere Ähnlichkeit zwischen einer Methode und einer Funktion besteht darin, dass die erstere leicht in die letztere umgewandelt werden kann:
val f = m _
Scala wird erweitert , dass unter der Annahme , m
Typ ist (List[Int])AnyRef
in (Scala 2.7):
val f = new AnyRef with Function1[List[Int], AnyRef] {
def apply(x$1: List[Int]) = this.m(x$1)
}
In Scala 2.8 wird tatsächlich eine AbstractFunction1
Klasse verwendet, um die Klassengröße zu reduzieren.
Beachten Sie, dass man nicht umgekehrt konvertieren kann - von einer Funktion zu einer Methode.
Methoden haben jedoch einen großen Vorteil (zwei - sie können etwas schneller sein): Sie können Typparameter empfangen . Während f
oben beispielsweise notwendigerweise der Typ des List
Empfangs angegeben werden kann ( List[Int]
im Beispiel), m
kann es beispielsweise parametrisiert werden:
def m[T](l: List[T]): String = l mkString ""
Ich denke, das deckt so ziemlich alles ab, aber ich werde dies gerne mit Antworten auf alle verbleibenden Fragen ergänzen.
val f = m
durch den Compiler zitieren val f = new AnyRef with Function1[List[Int], AnyRef] { def apply(x$1: List[Int]) = this.m(x$1) }
, sollten Sie darauf hinweisen, dass sich das this
Innere der apply
Methode nicht auf das AnyRef
Objekt bezieht , sondern auf das Objekt, in dessen Methode das val f = m _
ausgewertet wird ( sozusagen das Äußere) this
), da this
zu den Werten gehört, die vom Abschluss erfasst werden (wie z. B. return
wie unten angegeben).
Ein großer praktischer Unterschied zwischen einer Methode und einer Funktion ist, was return
bedeutet. return
kehrt immer nur von einer Methode zurück. Beispielsweise:
scala> val f = () => { return "test" }
<console>:4: error: return outside method definition
val f = () => { return "test" }
^
Die Rückgabe von einer in einer Methode definierten Funktion führt zu einer nicht lokalen Rückgabe:
scala> def f: String = {
| val g = () => { return "test" }
| g()
| "not this"
| }
f: String
scala> f
res4: String = test
Während die Rückkehr von einer lokalen Methode nur von dieser Methode zurückkehrt.
scala> def f2: String = {
| def g(): String = { return "test" }
| g()
| "is this"
| }
f2: String
scala> f2
res5: String = is this
for (a <- List(1, 2, 3)) { return ... }
? Das wird zu einer Schließung entzuckert.
return
einen Wert von der Funktion und eine Form von escape
oder break
oder continue
von Methoden zurückgegeben.
Funktion Eine Funktion kann mit einer Liste von Argumenten aufgerufen werden, um ein Ergebnis zu erzielen. Eine Funktion verfügt über eine Parameterliste, einen Body und einen Ergebnistyp. Funktionen, die Mitglieder einer Klasse, eines Merkmals oder eines Singleton-Objekts sind, werden als Methoden bezeichnet . In anderen Funktionen definierte Funktionen werden als lokale Funktionen bezeichnet. Funktionen mit dem Ergebnistyp der Einheit werden Prozeduren genannt. Anonyme Funktionen im Quellcode werden als Funktionsliterale bezeichnet. Zur Laufzeit werden Funktionsliterale in Objekte instanziiert, die als Funktionswerte bezeichnet werden.
Programmierung in Scala Second Edition. Martin Odersky - Lex Löffel - Bill Venners
Angenommen, Sie haben eine Liste
scala> val x =List.range(10,20)
x: List[Int] = List(10, 11, 12, 13, 14, 15, 16, 17, 18, 19)
Definieren Sie eine Methode
scala> def m1(i:Int)=i+2
m1: (i: Int)Int
Definieren Sie eine Funktion
scala> (i:Int)=>i+2
res0: Int => Int = <function1>
scala> x.map((x)=>x+2)
res2: List[Int] = List(12, 13, 14, 15, 16, 17, 18, 19, 20, 21)
Methode, die das Argument akzeptiert
scala> m1(2)
res3: Int = 4
Funktion mit val definieren
scala> val p =(i:Int)=>i+2
p: Int => Int = <function1>
Das Argument für die Funktion ist optional
scala> p(2)
res4: Int = 4
scala> p
res5: Int => Int = <function1>
Das Argument zur Methode ist obligatorisch
scala> m1
<console>:9: error: missing arguments for method m1;
follow this method with `_' if you want to treat it as a partially applied function
Überprüfen Sie das folgende Lernprogramm , in dem erläutert wird, wie andere Unterschiede anhand von Beispielen wie anderen Beispielen für diff mit Methode Vs-Funktion übergeben werden. Verwenden Sie die Funktion als Variablen und erstellen Sie eine Funktion, die die Funktion zurückgibt
Hier gibt es einen schönen Artikel , aus dem die meisten meiner Beschreibungen stammen. Nur ein kurzer Vergleich der Funktionen und Methoden in Bezug auf mein Verständnis. Ich hoffe es hilft:
Funktionen : Sie sind im Grunde ein Objekt. Genauer gesagt sind Funktionen Objekte mit einer Apply-Methode. Daher sind sie aufgrund ihres Overheads etwas langsamer als Methoden. Es ähnelt statischen Methoden in dem Sinne, dass sie unabhängig von einem aufzurufenden Objekt sind. Ein einfaches Beispiel für eine Funktion ist wie folgt:
val f1 = (x: Int) => x + x
f1(2) // 4
Die obige Zeile ist nichts anderes als das Zuweisen eines Objekts zu einem anderen wie object1 = object2. Tatsächlich ist das Objekt2 in unserem Beispiel eine anonyme Funktion und die linke Seite erhält aus diesem Grund den Typ eines Objekts. Daher ist jetzt f1 ein Objekt (Funktion). Die anonyme Funktion ist tatsächlich eine Instanz von Function1 [Int, Int], dh eine Funktion mit 1 Parameter vom Typ Int und einem Rückgabewert vom Typ Int. Wenn Sie f1 ohne die Argumente aufrufen, erhalten Sie die Signatur der anonymen Funktion (Int => Int =).
Methoden : Sie sind keine Objekte, sondern einer Instanz einer Klasse zugeordnet, dh einem Objekt. Genau das gleiche wie die Methode in Java oder die Member-Funktionen in c ++ (wie Raffi Khatchadourian in einem Kommentar zu dieser Frage hervorhob ) usw. Ein einfaches Beispiel für eine Methode ist wie folgt:
def m1(x: Int) = x + x
m1(2) // 4
Die obige Zeile ist keine einfache Wertzuweisung, sondern eine Definition einer Methode. Wenn Sie diese Methode mit dem Wert 2 wie in der zweiten Zeile aufrufen, wird das x durch 2 ersetzt und das Ergebnis wird berechnet und Sie erhalten 4 als Ausgabe. Hier erhalten Sie eine Fehlermeldung, wenn Sie einfach m1 schreiben, da es sich um eine Methode handelt und den Eingabewert benötigt. Mit _ können Sie einer Funktion wie unten eine Methode zuweisen:
val f2 = m1 _ // Int => Int = <function1>
Hier ist ein großartiger Beitrag von Rob Norris, der den Unterschied erklärt, hier ist ein TL; DR
Methoden in Scala sind keine Werte, aber Funktionen. Sie können eine Funktion erstellen, die über η-Erweiterung an eine Methode delegiert (ausgelöst durch den nachfolgenden Unterstrich).
mit folgender Definition:
ein Verfahren ist etwas mit definierten def und ein Wert ist etwas , das Sie zu einem zuweisen val
Kurz gesagt ( Auszug aus dem Blog ):
Wenn wir eine Methode definieren, sehen wir, dass wir sie nicht einer zuweisen können val
.
scala> def add1(n: Int): Int = n + 1
add1: (n: Int)Int
scala> val f = add1
<console>:8: error: missing arguments for method add1;
follow this method with `_' if you want to treat it as a partially applied function
val f = add1
Beachten Sie auch die Art von add1
, die nicht normal aussieht; Sie können keine Variable vom Typ deklarieren (n: Int)Int
. Methoden sind keine Werte.
Durch Hinzufügen des Postfix-Operators für die η-Erweiterung (η wird „eta“ ausgesprochen) können wir die Methode jedoch in einen Funktionswert umwandeln. Beachten Sie die Art von f
.
scala> val f = add1 _
f: Int => Int = <function1>
scala> f(3)
res0: Int = 4
Dies _
hat zur Folge, dass Folgendes ausgeführt wird: Wir erstellen eine Function1
Instanz, die an unsere Methode delegiert.
scala> val g = new Function1[Int, Int] { def apply(n: Int): Int = add1(n) }
g: Int => Int = <function1>
scala> g(3)
res18: Int = 4
In Scala 2.13 können Methoden im Gegensatz zu Funktionen Methoden übernehmen / zurückgeben
Diese Einschränkungen werden jedoch in dotty (Scala 3) durch die polymorphen Funktionstypen # 4672 aufgehoben. Beispielsweise aktiviert dotty Version 0.23.0-RC1 die folgende Syntax
Geben Sie Parameter ein
def fmet[T](x: List[T]) = x.map(e => (e, e))
val ffun = [T] => (x: List[T]) => x.map(e => (e, e))
Implizite Parameter ( Kontextparameter )
def gmet[T](implicit num: Numeric[T]): T = num.zero
val gfun: [T] => Numeric[T] ?=> T = [T] => (using num: Numeric[T]) => num.zero
Abhängige Typen
class A { class B }
def hmet(a: A): a.B = new a.B
val hfun: (a: A) => a.B = hmet
Weitere Beispiele finden Sie unter tests / run / polymorphic-functions.scala
In der Praxis muss ein Scala-Programmierer nur die folgenden drei Regeln kennen, um Funktionen und Methoden ordnungsgemäß verwenden zu können:
def
und Funktionsliterale definierte Methoden =>
sind Funktionen. Es ist in Seite 143, Kapitel 8 des Buches Programmieren in Scala, 4. Auflage, definiert.someNumber.foreach(println)
Nach vier Ausgaben von Programming in Scala ist es immer noch ein Problem für die Menschen, die beiden wichtigen Konzepte zu unterscheiden: Funktion und Funktionswert, da nicht alle Ausgaben eine klare Erklärung geben. Die Sprachspezifikation ist zu kompliziert. Ich fand die obigen Regeln einfach und genau.