Ich habe Schwierigkeiten herauszufinden, wie man den Sprung von einer Scala-Funktion mit höherer Ordnung zum Beispiel macht. Es wurde in dieser Diashow auf Folie 81 .
Hier ist die Definition der übergeordneten Funktion:
%Vor%Hier sind die Beispiele:
%Vor%Huh ?! Es muss nur ein paar Schritte hier sein, die ich vermisse. Ich verstehe, dass die Beispiele sowohl die Funktionsdefinition UND einige Scala-Feinheiten nutzen können. Ich habe einfach nicht genug Erfahrung Scala zu lesen und die verbindenden Annahmen noch zu machen.
Mein Hintergrund ist wie ein Java OO. Ich lerne jetzt Scala und funktionale Programmierung. Und das ist nicht das erste Beispiel, das ich nicht verstanden habe. Es ist nur der erste, in dem ich den Mut hatte zu posten, zu wissen, dass ich ignorant aussehe.
Ich habe versucht, das zu recherchieren. Zuerst ging ich zur Scala "Bibel", "Programmieren in Scala 2nd Edition" und versuchte, einen Sinn zu ergeben von wenn von dort (Seiten 165-9). Dann habe ich hier auf StackOverflow gesucht. Und ich habe mehrere Links gefunden, die sich in der Gegend herumsprechen. Aber nichts zeigt mir SCHRITT-FÜR-SCHRITT die Verbindung zwischen einer Scala High-Definition-Funktionsdefinition und den bereitgestellten Beispielen in einer Weise, die der bestimmten Instanz in dieser Folie entspricht.
Folgendes habe ich in StackOverflow gefunden:
Ich merke gerade, dass ich Google übersprungen habe und direkt zu StackOverflow gekommen bin. Hmmm. Wenn Sie googlen und den richtigen Link finden, würde ich mich freuen, ihn zu sehen. Ich habe keine Zeit mehr, alle Google-Links zu durchforsten, die Begriffe wie Affe-Monade, Blastomorphismen usw. verwenden, und lasse mich noch verwirrter und weniger wahrscheinlich versuchen, dies herauszufinden.
Ich denke, das Folgende ist nur eine Beispiel-Signatur, die zum Zweck der Anzeige einiger der Scala-Auflistungseigenschaften bereitgestellt wird. Insbesondere zeigt es keine Implementierung, so dass Sie nicht wirklich alle Punkte verbinden können. Es stimmt auch nicht wirklich mit den Beispielen überein ... Also, vielleicht ist das verwirrend.
%Vor% Ich würde das wie folgt lesen: Bei einer Collection-Klasse X
über Elemente vom Typ A
:
map
-Funktion, die für einen Typ B
parametrisiert ist
map
-Funktion übernimmt eine Funktion f
konvertiert eine einzelne A
in eine einzige B
map
gibt eine Sammlung des gleichen Typs X
über Elemente vom Typ B
zurück. Dann springt es zu dem Beispiel, um es zu veranschaulichen:
%Vor%Also, verbinde die Punkte:
X
ist der Typ von (1 bis 10), hier ein Range
f: A => B
ist x => x * 2
, was als eine Funktion abgeleitet wird, die ein Int
und returning und Int
. Range
über Int
zurückgegeben werden, aber dies liefert tatsächlich ein IndexedSeq
. Ein besseres Beispiel könnte sein:
%Vor%Eine Funktion höherer Ordnung (oder Methode) ist eine Funktion / Methode, die entweder eine Funktion als Parameter annimmt oder eine Funktion als Ergebnis oder beides ergibt.
In diesem Fall ist es eine Methode namens map
, die für Dinge wie Listen, Arrays und viele andere Arten von Containern definiert ist. Bei Aufruf von 1 to 10
, das ist ein Zahlenbereich von 1 bis 10, dargestellt durch Range[Int]
in Scala, durchläuft es sie nacheinander und wendet die Funktion (die als Parameter übergeben wurde) auf jede Zahl an innerhalb der Reichweite. Die Ergebnisse dieser Funktion werden in einem neuen Container gesammelt - Vector[Int]
in diesem Fall, der als Ergebnis der Methode map
zurückgegeben wird.
So (1 to 10) map { x => x * 2 }
, was gleichzeitig syntaktischer Zucker für (1 to 10).map(x => x * 2)
ist, gilt x => x * 2
für Zahlen von 1 to 10
. Sie können es sich als Rückruffunktion vorstellen. Du hättest es auch so schreiben können:
Definieren wir einen Datentyp mit einer Kartenmethode, einer einfach verknüpften Liste.
%Vor% Eine Liste ist entweder leer oder ein einzelner Wert ( head
) gepaart mit dem Rest der Liste ( tail
). Wir repräsentieren die beiden Fälle als eine Klassenhierarchie, die von einer versiegelten Elternklasse MyList
ausgeht.
Beachten Sie die Implementierung von Cons#map
, wir haben den Parameter f
zweimal verwendet, um erstens die Funktion mit head
auszuführen und zweitens den rekursiven Aufruf von tail.map
zu übernehmen.
Die Syntax f(head)
ist eine Abkürzung für f.apply(head)
, der Wert f
ist eine Instanz der Klasse Function1
, die die Methode apply
definiert hat.
So weit, so gut. Let.s eine Liste aufbauen.
%Vor% Nun wollen wir die Liste von Ints
in eine neue Liste von String
s konvertieren, indem wir jedes Element transformieren. In Java würden Sie Function1
explizit wie folgt anonym ableiten:
Das ist in Scala legal, aber das Signal-Rausch-Verhältnis ist nicht groß. Lassen Sie uns stattdessen die anonyme Funktionssyntax verwenden.
%Vor%Wir können weiter gehen und explizite Typ-Annotationen weglassen, wo der Compiler genug Informationen hat, um sie abzuleiten.
Lassen Sie uns zuerst den Typ des Parameters a
auf Basis des Elementtyps von list
herleiten.
Wirklich einfache Funktionen wie diese können auch mit der Platzhaltersyntax ausgedrückt werden. Anstatt die Parameter zu deklarieren, schreibe einfach den Code und verwende _
für eine unbekannte Menge. Dadurch können Sie auch den Typ von stringList4
ableiten:
Konzentrieren wir uns einfach auf die Methode map , die so definiert ist, wie sie auf Ihrem Merkmal X definiert ist
%Vor% Ok, das ist also eine Methode mit einem Parameter, f
. Der Typ von f
(das Bit nach dem Doppelpunkt) ist A => B
. Dies ist ein Funktionstyp. Es ist eine Abkürzung für das Merkmal Function1[A, B]
, aber ich ziehe es vor, überhaupt nicht an diese Eigenschaften zu denken und nur an etwas , das ein B für einen gegebenen Wert von A erzeugen kann.
Also: Eine Funktion A => B
ist etwas, das eine einzelne Instanz vom Typ A
benötigt und eine einzelne Instanz vom Typ B
erzeugt. Hier sind einige Beispiele für die Deklaration von
Denken Sie jetzt darüber nach, was X[A]
ist; könnte ein Sammlungstyp sein, z. B. List
. Wenn Sie eine List[String]
und die String => Int
Funktion g
oben besitzen, können Sie offensichtlich eine List[Int]
in erhalten, indem Sie die Funktion auf jedes Element in der Liste anwenden Erstellen einer neuen Liste mit den Ergebnissen.
So, jetzt könnte man sagen:
%Vor%Aber Scala erlaubt es Ihnen, die Funktion anonym zu deklarieren. Was bedeutet das? Nun, es bedeutet, dass Sie es an der Verwendungsstelle inline deklarieren können, anstatt es als val oder var deklarieren zu müssen. Oft werden diese "Lambdas" genannt. Die Syntax, die Sie sich wundern, sind zwei Optionen für solche Funktionen.
%Vor% Das sind im Grunde alle die gleichen Dinge. Der erste erklärt eindeutig die Funktion, die an die Karte übergeben wird. Da scala eine Inferenz hat, können Sie in diesem Anwendungsfall den Typ der Funktionseingabe weglassen. Die Platzhaltersyntax _ wird anstelle des Bezeichners x
verwendet und ist ein nettes Stück Zucker, wenn Sie nur einmal auf die Eingabe verweisen müssen. Und Sie können in vielen Fällen anstelle von geschweiften Klammern auch Parens verwenden, mit Ausnahme von Funktionen mit mehreren Anweisungen.
Ihr Beispiel bezieht sich auf das Scala Collection Framework, das selbst eine ausgefeilte Verwendung des Typsystems hat, um beim Transformieren einer Sammlung den spezifischsten Typ zu erhalten. Nun, der Mechanismus , der dies ermöglicht, ist schwer zu verstehen und ist es nicht wirklich relevant für das Beispiel. Funktion höherer Ordnung ist einfach eine Funktion oder Methode , die als Argument (oder gibt zurück) andere Funktionen . Das Beispiel wird durch Hinzufügen von Typparametern und Nichtbenennung der Verwendung von implicits durch Scala Collection Frameworks etwas verdeckt.
Nicht sicher, was genau Sie nicht bekommen, aber die Beispiele zu erklären:
%Vor% A trait
ist wie eine Java-Schnittstelle, die auch konkrete Methoden haben kann.
X
ist der Name der Eigenschaft und [A]
ist ein Typparameter - denke Java generics <A>
. (Oft werden A
, B
usw. für Elementtypen in Sammlungen und T
anderswo verwendet, aber das ist nur eine Konvention.)
Das Merkmal gibt ein Element namens map
an, das eine Methode mit einem anderen Typparameter [B]
ist und ein Funktionsargument vom Typ A => B
mit dem Rückgabetyp X[B]
verwendet. Es ist hier abstrakt, weil es keinen Methodenkörper gibt.
Das fehlende Bit ist, dass A => B
für Function1[A, B]
kurz ist. Function1
ist der Typ der Funktionsobjekte, die 1 Argument haben. (A, B) => C
ist die Abkürzung für Function2[A, B, C]
usw. Sie können Ihre eigenen Function
-Typen in Java erstellen - das macht Spaß. Ein Funktionsobjekt ist im Wesentlichen nur eines, das eine apply
-Methode hat, die ein Ergebnis von einigen Argumenten erzeugt.
Diese enthalten eine Punktfreie Notation, wobei a.method(b)
als a method b
geschrieben wird. Also to
ist eine Methode für RichInt
, die ein Int
übernimmt und ein Range
erzeugt. map
ist eine Methode für Range
, die ein Function1-Argument verwendet (denken Sie daran, eine Funktion ist nur ein Objekt vom Typ Function1
).
=>
wird auch verwendet, um die Funktionen selbst zu schreiben (zusätzlich zu der Typ-Ebene, wie oben beschrieben). Das Folgende ist also das gleiche, alle Objekte vom Typ Int => Int
:
Scala verwendet Typ-Inferenz , so dass Sie nicht alle Typen selbst hinzufügen müssen, wenn ein bestimmter Funktionstyp (oder ein anderer parametrisierter Typ) vom Kontext erwartet wird, z. B.
%Vor% Hoffentlich können Sie sehen, was diese Unterstrichnotation bedeutet. Der Unterstrich ist nützlich, da sonst immer eine gewisse Wiederholung auftritt, da der RHS einer Funktionsdefinition die Parameter aus dem LHS verwenden muss. Ein anderes Beispiel könnte eine Funktion sein, die eine String
auf ihre Länge abbildet:
Da Vals normalerweise abgeleitet werden, können Sie nur die notwendige Typ-Annotation mit
angeben %Vor%Es ist wahrscheinlich ein wenig verwirrend, weil die syntaktische Zucker- und Typinferenz bedeutet, dass es mehrere Möglichkeiten gibt, das gleiche zu schreiben, aber sobald Sie es bekommen, werden Sie feststellen, dass es Ihr Leben einfacher macht und den Lärm reduziert. Spielen Sie gut mit diesen in der REPL, wenn Sie es nicht schon sind.
Scala: (1 to 10) map { x => x * 2 }
Deutsch: Nimm die Werte 1 bis 10 und multipliziere sie jeweils mit 2.
Einige Dinge zu beachten:
(1 bis 10), Scala erkennt, dass dies eine Sammlung von Ganzzahlen ist, speziell Range [Int]. Es kann in einen anderen Sammlertyp umgewandelt werden, z. (1 to 10).toList
map ist kleingeschrieben. Denke an Verb, um von einem Ding zum anderen zu mappen.
{x = & gt; x * 2}, ist von geschweiften Klammern umgeben. Das bedeutet, es handelt sich um eine Funktion ohne einen Namen, eine anonyme Funktion .
Unterstrich (_) kann für x => x
Scala: trait X[A] { def map[B](f: A => B): X[B] }
Englisch:
Wir definieren eine Eigenschaft, die wir einer Klasse von X hinzufügen können, geben Sie A ein.
Es hat eine Methode, die einen Wert annimmt und ihn in einen anderen Wert für eine neue Klasse von X abbildet.
Hinweis:
X[A]
und X[B]
gehören zum selben Sammlertyp, können jedoch Elemente unterschiedlichen Typs enthalten, z. '(1 bis 10) .toList map {_.toSTring} wird List [Int] List [String] zuordnen.
f: A => B
, dies bedeutet, dass map eine Funktion als Argument übernimmt und einen Parameter vom Typ A hat und den Typ B zurückgibt.
map
ist in allen Scala-Sammlungsarten definiert. Normalerweise würden Sie das nicht selbst definieren.
Tags und Links scala java higher-order-functions