Ich sehe java.util.function.BiFunction, also kann ich das tun:
BiFunction<Integer, Integer, Integer> f = (x, y) -> { return 0; };
Was ist, wenn das nicht gut genug ist und ich TriFunction brauche? Es existiert nicht!
TriFunction<Integer, Integer, Integer, Integer> f = (x, y, z) -> { return 0; };
Ich denke, ich sollte hinzufügen, dass ich weiß, dass ich meine eigene TriFunction definieren kann. Ich versuche nur zu verstehen, warum sie nicht in die Standardbibliothek aufgenommen wurde.
Antworten:
Soweit ich weiß, gibt es nur zwei Arten von Funktionen, destruktive und konstruktive.
Während konstruktive Funktionen, wie der Name schon sagt, etwas konstruieren, zerstört eine destruktive Funktion etwas, aber nicht so, wie Sie jetzt vielleicht denken.
Zum Beispiel die Funktion
ist konstruktiv . Da musst du etwas konstruieren. Im Beispiel haben Sie das Tupel (x, y) konstruiert . Konstruktive Funktionen haben das Problem, unendliche Argumente nicht verarbeiten zu können. Aber das Schlimmste ist, man kann nicht einfach ein Argument offen lassen. Sie können nicht einfach "gut, lassen Sie x: = 1" sagen und jedes y ausprobieren, das Sie ausprobieren möchten. Sie müssen jedes Mal das ganze Tupel mit konstruieren
x := 1
. Wenn Sie also sehen möchten, was die Funktionen füry := 1, y := 2, y := 3
Sie zurückgeben, müssen Sie schreibenf(1,1) , f(1,2) , f(1,3)
.In Java 8 sollten konstruktive Funktionen (meistens) mithilfe von Methodenreferenzen behandelt werden, da die Verwendung einer konstruktiven Lambda-Funktion keinen großen Vorteil bietet. Sie sind ein bisschen wie statische Methoden. Sie können sie verwenden, aber sie haben keinen wirklichen Zustand.
Der andere Typ ist der zerstörerische, er nimmt etwas und zerlegt es so weit wie nötig. Zum Beispiel die destruktive Funktion
macht das gleiche wie die Funktion,
f
die konstruktiv war. Die Vorteile einer destruktiven Funktion sind, dass Sie jetzt unendlich viele Argumente verarbeiten können, was besonders für Streams praktisch ist, und dass Sie Argumente einfach offen lassen können. Wenn Sie also noch einmal sehen möchten, wie das Ergebnis wäre, wennx := 1
undy := 1 , y := 2 , y := 3
, können Sie sagenh = g(1)
undh(1)
ist das Ergebnis füry := 1
,h(2)
füry := 2
undh(3)
füry := 3
.Hier haben Sie also einen festen Zustand! Das ist ziemlich dynamisch und das ist meistens das, was wir von einem Lambda wollen.
Muster wie Factory sind viel einfacher, wenn Sie nur eine Funktion eingeben können, die die Arbeit für Sie erledigt.
Zerstörerische lassen sich leicht miteinander kombinieren. Wenn der Typ stimmt, können Sie sie einfach nach Ihren Wünschen zusammenstellen. Auf diese Weise können Sie leicht Morphismen definieren, die das Testen (mit unveränderlichen Werten) erheblich erleichtern!
Sie können das auch mit einer konstruktiven Komposition tun, aber eine destruktive Komposition sieht besser aus und ähnelt eher einer Liste oder einem Dekorateur, und die konstruktive Komposition ähnelt einem Baum. Und Dinge wie Backtracking mit konstruktiven Funktionen sind einfach nicht schön. Sie können nur die Teilfunktionen einer destruktiven (dynamische Programmierung) speichern und auf "Backtrack" einfach die alte destruktive Funktion verwenden. Das macht Code viel kleiner und besser lesbar. Mit konstruktiven Funktionen müssen Sie sich mehr oder weniger an alle Argumente erinnern, was sehr viel sein kann.
Warum
BiFunction
sollte es also mehr Fragen geben als warum gibt es keineTriFunction
?Erstens haben Sie viel Zeit nur ein paar Werte (weniger als 3) und benötigen nur ein Ergebnis, sodass eine normale destruktive Funktion überhaupt nicht benötigt wird, eine konstruktive würde gut funktionieren. Und es gibt Dinge wie Monaden, die wirklich eine konstruktive Funktion brauchen. Aber abgesehen davon gibt es nicht wirklich viele gute Gründe, warum es
BiFunction
überhaupt einen gibt. Was nicht heißt, dass es entfernt werden sollte! Ich kämpfe für meine Monaden, bis ich sterbe!Wenn Sie also viele Argumente haben, die Sie nicht zu einer logischen Containerklasse kombinieren können, und wenn die Funktion konstruktiv sein soll, verwenden Sie eine Methodenreferenz. Wenn Sie andernfalls versuchen, die neu gewonnene Fähigkeit destruktiver Funktionen zu nutzen, werden Sie möglicherweise viele Dinge mit viel weniger Codezeilen tun.
quelle
BiFunction
wurde entwickelt, um eine einfache Datenreduktion zu ermöglichen, und die meistenStream
Terminaloperationen sind nur Datenreduktionen. Ein gutes Beispiel istBinaryOperator<T>
, in vielen verwendetCollectors
. Ein erstes Element wird mit dem zweiten reduziert, das dann mit dem nächsten reduziert werden kann, und so weiter. Natürlich können Sie auch einenFunction<T, Function<T, T>
func = x -> (y -> / * Reduktionscode hier * /) erstellen. Aber ernsthaft? All dies, wenn Sie es einfach tun könnenBinaryOperator<T> func = (x, y) -> /*reduction code here*/
. Außerdem scheint mir dieser Datenreduktionsansatz Ihrem "destruktiven" Ansatz sehr ähnlich zu sein.Function<Integer,Integer> f = (x,y) -> x + y
Java gültig ist, was es nicht ist. Das sollte zunächst eine BiFunktion sein!Wenn Sie TriFunction benötigen, gehen Sie einfach folgendermaßen vor:
Das folgende kleine Programm zeigt, wie es verwendet werden kann. Denken Sie daran, dass der Ergebnistyp als letzter generischer Typparameter angegeben wird.
Ich denke, wenn es eine praktische Verwendung für TriFunction in gegeben hätte
java.util.*
oderjava.lang.*
es definiert worden wäre. Ich würde jedoch nie über 22 Argumente hinausgehen ;-) Was ich damit meine, alle neuen Codes, die das Streamen von Sammlungen ermöglichen, erforderten niemals TriFunction als einen der Methodenparameter. Es war also nicht enthalten.AKTUALISIEREN
Der Vollständigkeit halber und gemäß der Erklärung der destruktiven Funktionen in einer anderen Antwort (in Bezug auf Currying) kann TriFunction wie folgt ohne zusätzliche Schnittstelle emuliert werden:
Natürlich ist es möglich, Funktionen auf andere Weise zu kombinieren, z.
Während Currying für jede Sprache selbstverständlich ist, die funktionale Programmierung über Lambdas hinaus unterstützt, ist Java nicht auf diese Weise aufgebaut, und obwohl dies erreichbar ist, ist der Code schwer zu pflegen und manchmal zu lesen. Als Übung ist es jedoch sehr hilfreich, und manchmal haben Teilfunktionen einen rechtmäßigen Platz in Ihrem Code.
quelle
TriFunction<Integer,Integer,Integer,Integer> comp = (x,y,z) -> x + y + z; comp = comp.andThen(s -> s * 2); int result = comp.apply(1, 2, 3); //12
Siehe stackoverflow.com/questions/19834611/…andThen()
Anwendungsbeispiel zur Antwort hinzugefügt .BiFunction
wird in derStream
API verwendet, um eine Datenreduktion durchzuführen, die dem Currying-Ansatz für mich sehr ähnlich ist: Sie nehmen nie mehr als zwei Argumente, und Sie können eine beliebige Anzahl von Elementen nacheinander verarbeiten (siehe meinen Kommentar zur akzeptierten Antwort, ich würde mich freuen zu wissen, wenn ich mich irre, wenn ich das so sehe).Alternativ können Sie die folgende Abhängigkeit hinzufügen:
Jetzt können Sie die Vavr-Funktion verwenden, wie unten bis zu 8 Argumente.
3 Argumente:
5 Argumente:
quelle
vavr
Bibliothek besser dran sind - dies macht die funktionale Programmierung in Java so erträglich wie möglich.Ich habe fast die gleiche Frage und eine teilweise Antwort. Ich bin mir nicht sicher, ob die konstruktive / dekonstruktive Antwort den Sprachdesignern entspricht. Ich denke, 3 und mehr bis zu N zu haben, hat gültige Anwendungsfälle.
Ich komme aus .NET. und in .NET haben Sie Func und Action für ungültige Funktionen. Prädikat und einige andere Sonderfälle existieren ebenfalls. Siehe: https://msdn.microsoft.com/en-us/library/bb534960(v=vs.110).aspx
Ich frage mich, warum die Sprachdesigner sich für Function, Bifunction entschieden haben und erst mit DecaExiFunction fortfuhren.
Die Antwort auf den zweiten Teil lautet Typlöschung. Nach der Kompilierung gibt es keinen Unterschied zwischen Func und Func. Folgendes wird daher nicht kompiliert:
Innere Funktionen wurden verwendet, um ein weiteres kleines Problem zu umgehen. Eclipse bestand darauf, beide Klassen in Dateien mit dem Namen Function im selben Verzeichnis zu haben ... Ich bin mir nicht sicher, ob dies heutzutage ein Compiler-Problem ist. Aber ich kann den Fehler in Eclipse nicht beheben.
Func wurde verwendet, um Namenskonflikte mit dem Java-Funktionstyp zu verhindern.
Wenn Sie also Func aus 3 bis 16 Argumenten hinzufügen möchten, können Sie zwei Dinge tun.
Beispiel für den zweiten Weg:
und
Was wäre der beste Ansatz?
In den obigen Beispielen habe ich keine Implementierungen für die Methoden andThen () und compose () aufgenommen. Wenn Sie diese hinzufügen, müssen Sie jeweils 16 Überladungen hinzufügen: Der TriFunc sollte ein andthen () mit 16 Argumenten haben. Das würde Ihnen einen Kompilierungsfehler aufgrund zirkulärer Abhängigkeiten geben. Außerdem hätten Sie diese Überladungen für Function und BiFunction nicht. Daher sollten Sie Func auch mit einem Argument und Func mit zwei Argumenten definieren. In .NET würden zirkuläre Abhängigkeiten mithilfe von Erweiterungsmethoden umgangen, die in Java nicht vorhanden sind.
quelle
andThen
mit 16 Argumenten? Das Ergebnis einer Funktion in Java ist ein einzelner Wert.andThen
nimmt diesen Wert und macht etwas damit. Es gibt auch kein Problem mit der Benennung. Klassennamen sollten unterschiedlich sein und sich in verschiedenen Dateien mit demselben Namen befinden - gemäß der Logik, die von Java-Sprachentwicklern mit Function und BiFunction festgelegt wurde. Alle diese unterschiedlichen Namen werden auch benötigt, wenn die Argumenttypen unterschiedlich sind. Man kann einenVargFunction(T, R) { R apply(T.. t) ... }
für einen einzigen Typ erstellen .Ich habe den Quellcode für BiFunction hier gefunden:
https://github.com/JetBrains/jdk8u_jdk/blob/master/src/share/classes/java/util/function/BiFunction.java
Ich habe es geändert, um TriFunction zu erstellen. Wie BiFunction verwendet es andThen () und nicht compose (). Daher ist es für einige Anwendungen, für die compose () erforderlich ist, möglicherweise nicht geeignet. Es sollte für normale Arten von Objekten in Ordnung sein. Einen guten Artikel zu andThen () und compose () finden Sie hier:
http://www.deadcoderising.com/2015-09-07-java-8-functional-composition-using-compose-and-andthen/
quelle
Sie können auch Ihre eigene Funktion mit den 3 Parametern erstellen
quelle
Sie können nicht immer bei TriFunction anhalten. Manchmal müssen Sie möglicherweise n Parameter an Ihre Funktionen übergeben. Dann muss das Support-Team eine QuadFunction erstellen, um Ihren Code zu reparieren. Eine langfristige Lösung wäre, ein Objekt mit den zusätzlichen Parametern zu erstellen und dann die vorgefertigte Funktion oder BiFunktion zu verwenden.
quelle