TL;DR Java-Version der Sequenzfunktion und der Traversenfunktion, die in "Scala Function Type Design & Programming" angezeigt wird Scala Functional Design & Programming - Eine ausführliche Anleitung zur Funktionsweise von Scalaz Contributor
Wenn Sie eine Liste mit "Optional" erhalten und alle Werte haben (= isPresent ist wahr), möchten Sie eine Liste mit Werten ohne "Optional" erstellen. Wenn auch nur ein Wert "null" ist (= isPresent ist falsch), möchte ich "Optional.empty" zurückgeben.
Ich denke, Sie können es ungefähr so schreiben. Wenn das Argument oder Listenelement null enthält, tritt ein Fehler auf, der hier jedoch nicht berücksichtigt wird. (Es ist schlimmer, ~~ null ~~ zu verwenden)
public static <T> Optional<List<T>> sequence(List<Optional<T>> xs) {
List<T> ys = new ArrayList<>();
for (Optional<T> x : xs) {
//Wenn es ein Element gibt, das keinen Wert hat, wird die Verarbeitung an diesem Punkt beendet.
if(x.isEmpty()) {
return Optional.empty();
}
//Wenn der Wert vorhanden ist, packen Sie Optional aus und fügen Sie ihn der Liste hinzu
ys.add(x.get());
}
//Umschließt eine Liste mit nur Werten mit Optional und gibt sie zurück
return Optional.of(ys);
}
List<Optional<String>> xs = List.of(Optional.of("foo"), Optional.of("bar"));
List<Optional<Integer>> ys = List.of(Optional.of(1), Optional.empty(), Optional.of(100));
List<Optional<Boolean>> zs = List.of(); //Leere Liste
System.out.println(sequence(xs)); // Optional[[foo, bar]]
System.out.println(sequence(ys)); // Optional.empty
System.out.println(sequence(zs)); // Optional[[]]
Es scheint richtig zu funktionieren.
Angenommen, Sie haben die folgende Funktion, die eine Zeichenfolge in eine Zahl konvertiert: Wenn die Zielzeichenfolge in einen numerischen Wert konvertiert werden kann, umschließt diese Funktion den numerischen Wert mit "Optional" und gibt ihn zurück. Wenn die Konvertierung fehlschlägt, wird "Optional.empty" zurückgegeben. [^ 1]
public static Optional<Integer> safeParseInt(String s) {
try {
Integer n = Integer.valueOf(s);
return Optional.of(n);
} catch (NumberFormatException e) {
return Optional.empty();
}
}
Verwenden Sie diese Funktion, um eine Liste von String
s in eine Liste von Optional <Integer>
zu konvertieren.
Wenn das Element "Optional.empty" enthält, z. B. beim Anwenden der Sequenzfunktion, ziehen Sie eine Funktion in Betracht, die auch das Gesamtergebnis "Optional.empty" ergibt.
Nennen Sie es "durchqueren".
//Dies ist alles in Zahlen konvertierbar, also optional[[1, 100, 30]]Möchte
Optional<List<Integer>> xs = traverse(List.of("1", "100", "30"));
//Dies ist optional, da es Werte enthält, die nicht in Zahlen konvertiert werden können..Ich möchte es leer machen
Optional<List<Integer>> ys = traverse(List.of("1", "3", "hoge", "0", ""));
Dies könnte unter Verwendung der obigen Sequenzfunktion wie folgt implementiert werden.
public static Optional<List<Integer>> traverse(List<String> xs) {
//Einmal optional<Integer>In eine Liste von konvertieren
List<Optional<Integer>> ys = new ArrayList<>();
for (String x : xs) {
Optional<Integer> o = safeParseInt(x);
ys.add(o);
}
//Liste mit Sequenzfunktion<Optional<Integer>>Optional<List<Integer>>Umstellung auf
return sequence(ys);
}
Wenn Sie es verschieben, sehen Sie, dass Sie das gewünschte Ergebnis erhalten.
System.out.println(traverse(List.of("1", "100", "30"))); // Optional[[1, 100, 30]]
System.out.println(traverse(List.of("1", "3", "hoge", "0", ""))); // Optional.empty
Das Obige ist jedoch etwas verschwenderisch. Siehe folgendes Beispiel. Die Travase-Funktion wird inline und basierend auf den Zwischenergebnissen ausgegeben.
List<String> xs = List.of("1", "hoge", "2", "3", "4", "5", "6", "7", "8", "9");
//Dies ist genau die gleiche Verarbeitung wie die Travarse-Funktion
List<Optional<Integer>> ys = new ArrayList<>();
for (String x : xs) {
Optional<Integer> o = safeParseInt(x);
ys.add(o);
}
Optional<List<Integer>> zs = sequence(ys);
//Zwischenliste und Ergebnisse ausgeben
System.out.println(ys); // [Optional[1], Optional.empty, Optional[2], Optional[3], Optional[4], Optional[5], Optional[6], Optional[7], Optional[8], Optional[9]]
System.out.println(zs); // Optional.empty
Die Tatsache, dass "zs" zu "Optional.empty" wird, ist wie erwartet. Dies hat jedoch eine Zwischenliste "ys" erzeugt, die dieselbe Länge wie "xs" hat. Es ist eine Entscheidung, die safeParseInt-Funktion auf das zweite Element "hoge" von "xs" anzuwenden, und wenn "Optional.empty" abgeschlossen ist, lautet das Gesamtergebnis auch "Optional.empty". Es fühlt sich etwas ineffizient an, den Konvertierungsprozess fortzusetzen, nachdem das Ergebnis bekannt ist. Ich denke nicht, dass es ein Problem ist, wenn die Anzahl der Elemente klein ist, aber wenn die Anzahl der Elemente groß ist, kann dies die Leistung beeinträchtigen. Ich möchte "Optional.empty" sofort zurückgeben, ohne die Funktion safeParseInt auf die Elemente anzuwenden, nachdem sie zu "Optional.empty" geworden sind.
Schreiben Sie die Travarse-Funktion so um, dass sie funktioniert.
public static Optional<List<Integer>> traverse(List<String> xs) {
List<Integer> ys = new ArrayList<>();
for (String x : xs) {
//Konvertieren Sie Zeichenfolgen in Zahlen
Optional<Integer> o = safeParseInt(x);
//Wenn die Konvertierung fehlschlägt, wird der Prozess an diesem Punkt beendet.
if(o.isEmpty()) {
return Optional.empty();
}
//Wenn die Konvertierung erfolgreich ist, packen Sie Optional aus und fügen Sie sie der Liste hinzu
ys.add(o.get());
}
//Umschließt eine Liste mit nur Zahlen mit Optional und gibt sie zurück
return Optional.of(ys);
}
Ich habe die Sequenzfunktion nicht mehr verwendet und sowohl der Konvertierungsprozess als auch die Verzweigung werden in einer Schleife ausgeführt. Wenn die Konvertierung in der Mitte fehlschlägt, wird "Optional.empty" sofort an diesem Punkt zurückgegeben. Machen Sie safeParseInt zu einem Parameter, damit Sie jede Funktion verwenden können, die in "Optional" konvertiert werden kann.
public static <T, R> Optional<List<R>> traverse(List<T> xs, Function<T, Optional<R>> f) {
List<R> ys = new ArrayList<>();
for (T x : xs) {
Optional<R> o = f.apply(x);
if(o.isEmpty()) {
return Optional.empty();
}
ys.add(o.get());
}
return Optional.of(ys);
}
Übergeben Sie es bei Verwendung als Lambda-Ausdruck oder Methodenreferenz. Zusammenfassend sieht es so aus:
Main.java
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
//Lambda-Stil
Optional<List<Integer>> xs = traverse(List.of("1", "100", "30"), x -> safeParseInt(x));
//Methodenreferenz
Optional<List<Integer>> ys = traverse(List.of("1", "3", "hoge", "0"), Main::safeParseInt);
System.out.println(xs); // Optional[[1, 100, 30]]
System.out.println(ys); // Optional.empty
}
public static Optional<Integer> safeParseInt(String s) {
try {
Integer n = Integer.valueOf(s);
return Optional.of(n);
} catch (NumberFormatException e) {
return Optional.empty();
}
}
public static <T, R> Optional<List<R>> traverse(List<T> xs, Function<T, Optional<R>> f) {
List<R> ys = new ArrayList<>();
for (T x : xs) {
Optional<R> o = f.apply(x);
if(o.isEmpty()) {
return Optional.empty();
}
ys.add(o.get());
}
return Optional.of(ys);
}
//Wird nicht mehr verwendet
// public static <T> Optional<List<T>> sequence(List<Optional<T>> xs) {
// List<T> ys = new ArrayList<>();
// for (Optional<T> x : xs) {
// if(x.isEmpty()) {
// return Optional.empty();
// }
// ys.add(x.get());
// }
// return Optional.of(ys);
// }
}
Ich habe aufgehört, die Sequenzfunktion über die Travarse-Funktion aufzurufen, aber tatsächlich kann ich diese neue Travarse-Funktion verwenden, um die Sequenzfunktion wie folgt neu zu implementieren.
public static <T> Optional<List<T>> sequence(List<Optional<T>> xs) {
return traverse(xs, Function.identity()); // traverse(xs, x -> x)Aber ja
}
Es mag etwas seltsam sein, aber hier ist "Function.identity ()" (oder "x-> x" [^ 2]) eine Funktion, die "Optional
Lassen Sie uns zur Erklärung den Typparameter der Travarse-Funktion mit dieser neuen Sequenzfunktion abgleichen.
public static <T> Optional<List<T>> traverse(List<Optional<T>> xs, Function<Optional<T>, Optional<T>> f) {
List<T> ys = new ArrayList<>();
for (Optional<T> x : xs) {
Optional<T> o = f.apply(x); //f ist Funktion#Da es sich um Identität handelt, sind x und o dieselbe Instanz
if(o.isEmpty()) {
return Optional.empty();
}
ys.add(o.get()); //Immerhin ist es dasselbe wie das Packen des Inhalts von x
}
return Optional.of(ys);
}
Wie bei der zu Beginn definierten Sequenzfunktion können Sie sehen, dass wir gerade die Option von jedem Element von List <Optional <T >>
entfernt haben.
Ich glaube nicht, dass Java Allzweckfunktionen definiert, die diese Funktionen als Argumente verwenden (es ist schwierig zu standardisieren, da es viele Schnittstellen vom Funktionstyp gibt), aber ich habe irgendwie darüber nachgedacht, also habe ich es geschrieben. Abgesehen von seiner Praktikabilität wird es empfohlen, weil es Spaß macht, Gehirnübungen zu machen.
Danke, dass Sie bis zum Ende für mich gelesen haben. Wenn Sie Fragen oder Mängel haben, kontaktieren Sie uns bitte im Kommentarbereich oder unter Twitter.
[^ 1]: Optional T
in travarse entspricht Optional <T>
nacheinander. Da die Typparameter "T, R" von travarse vom gleichen Typ sein können, erfüllt der Typ "Function <Optional
Recommended Posts