TL;DR Version Java de la fonction de séquence et de la fonction de déplacement apparaissant dans "Conception et programmation du type de fonction Scala" Scala Functional Design & Programming - Un guide détaillé des fonctionnalités par Scalaz Contributor
Si vous recevez une liste de ʻOptional et que tous ont des valeurs (= isPresent est vrai), vous voulez faire une liste de valeurs sans ʻOptional
.
Si au moins une valeur est null
(= isPresent est false), nous voulons retourner ʻOptional.empty`.
En gros, je pense que vous pouvez l'écrire comme ça. Si l'argument ou l'élément de liste contient null, une erreur se produira, mais cela n'est pas pris en compte ici. (C'est pire d'utiliser ~~ null ~~)
public static <T> Optional<List<T>> sequence(List<Optional<T>> xs) {
List<T> ys = new ArrayList<>();
for (Optional<T> x : xs) {
//S'il y a un élément qui n'a pas de valeur, le traitement est terminé à ce point
if(x.isEmpty()) {
return Optional.empty();
}
//Si la valeur existe, décompressez Facultatif et ajoutez-la à la liste
ys.add(x.get());
}
//Enveloppe une liste de valeurs uniquement avec facultatif et la renvoie
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(); //Liste vide
System.out.println(sequence(xs)); // Optional[[foo, bar]]
System.out.println(sequence(ys)); // Optional.empty
System.out.println(sequence(zs)); // Optional[[]]
Cela semble fonctionner correctement.
Supposons que vous ayez la fonction suivante qui convertit une chaîne en nombre:
Si la chaîne de caractères cible peut être convertie en une valeur numérique, cette fonction encapsule la valeur numérique dans ʻOptional et la renvoie. Si la conversion échoue, ʻOptional.empty
est renvoyé. [^ 1]
public static Optional<Integer> safeParseInt(String s) {
try {
Integer n = Integer.valueOf(s);
return Optional.of(n);
} catch (NumberFormatException e) {
return Optional.empty();
}
}
Utilisez cette fonction pour convertir une liste de String
s en une liste de ʻOptional . De plus, si l'élément contient ʻOptional.empty
, comme lors de l'application de la fonction de séquence, considérez une fonction qui rend également le résultat global ʻOptional.empty`.
Nommez-le «traversée».
//Tout cela est convertible en nombres, donc facultatif[[1, 100, 30]]Vouloir
Optional<List<Integer>> xs = traverse(List.of("1", "100", "30"));
//Ceci est facultatif car il contient des valeurs qui ne peuvent pas être converties en nombres..Je veux le rendre vide
Optional<List<Integer>> ys = traverse(List.of("1", "3", "hoge", "0", ""));
Cela pourrait être implémenté comme suit en utilisant la fonction de séquence ci-dessus.
public static Optional<List<Integer>> traverse(List<String> xs) {
//Une fois facultatif<Integer>Convertir en une liste de
List<Optional<Integer>> ys = new ArrayList<>();
for (String x : xs) {
Optional<Integer> o = safeParseInt(x);
ys.add(o);
}
//Liste avec fonction de séquence<Optional<Integer>>Optionnel<List<Integer>>Conversion en
return sequence(ys);
}
Si vous le déplacez, vous verrez que vous obtenez le résultat souhaité.
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
Cependant, ce qui précède est un peu inutile. Voir l'exemple suivant. La fonction travase est intégrée et sortie en fonction des résultats intermédiaires.
List<String> xs = List.of("1", "hoge", "2", "3", "4", "5", "6", "7", "8", "9");
//C'est exactement le même traitement que la fonction travarse
List<Optional<Integer>> ys = new ArrayList<>();
for (String x : xs) {
Optional<Integer> o = safeParseInt(x);
ys.add(o);
}
Optional<List<Integer>> zs = sequence(ys);
//Sortie de la liste intermédiaire et des résultats
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
Le fait que zs
devienne ʻOptional.empty est comme prévu. Cependant, cela a généré une liste intermédiaire «ys» qui est de la même longueur que «xs». C'est une décision d'appliquer la fonction safeParseInt au deuxième élément "" hoge "" de
xs, et quand ʻOptional.empty
est terminé, le résultat global sera également ʻOptional.empty. Il semble un peu inefficace de continuer le processus de conversion après avoir connu le résultat. Je ne pense pas que ce soit un problème si le nombre d'éléments est petit, mais si le nombre d'éléments est grand, cela peut affecter les performances. Lorsque ʻOptional.empty
est atteint, la fonction safeParseInt n'est pas appliquée aux éléments après cela, et ʻOptional.empty` est renvoyé immédiatement.
Réécrivez la fonction travarse pour qu'elle le fasse.
public static Optional<List<Integer>> traverse(List<String> xs) {
List<Integer> ys = new ArrayList<>();
for (String x : xs) {
//Convertir des chaînes en nombres
Optional<Integer> o = safeParseInt(x);
//Si la conversion échoue, le processus sera interrompu à ce stade.
if(o.isEmpty()) {
return Optional.empty();
}
//Si la conversion réussit, déballer Facultatif et ajouter à la liste
ys.add(o.get());
}
//Enveloppe une liste de numéros uniquement avec facultatif et la renvoie
return Optional.of(ys);
}
J'ai arrêté d'utiliser la fonction de séquence et le processus de conversion et le branchement se font en une seule boucle.
Maintenant, si la conversion échoue au milieu, ʻOptional.empty sera renvoyé immédiatement à ce stade. Faites de safeParseInt un paramètre pour que vous puissiez utiliser n'importe quelle fonction qui peut être convertie en ʻOptional
.
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);
}
Lorsque vous l'utilisez, passez-le en tant qu'expression lambda ou référence de méthode. En résumé, cela ressemble à ceci:
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) {
//Style Lambda
Optional<List<Integer>> xs = traverse(List.of("1", "100", "30"), x -> safeParseInt(x));
//Référence de la méthode
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);
}
//Plus utilisé
// 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);
// }
}
J'ai arrêté d'appeler la fonction de séquence à partir de la fonction travarse, mais en fait, je peux utiliser cette nouvelle fonction travarse pour réimplémenter la fonction de séquence comme suit.
public static <T> Optional<List<T>> sequence(List<Optional<T>> xs) {
return traverse(xs, Function.identity()); // traverse(xs, x -> x)Mais oui
}
Cela peut être un peu étrange, mais ici Function.identity ()
(ou x-> x
[^ 2]) est une fonction qui prend ʻOptional et renvoie ʻOptional <T>
. , Le deuxième argument de Travarse Function <T, Optional <R >> f
peut être écrit comme ci-dessus, tant que le type de retour est ʻOptional`. [^ 3]
Par souci d'explication, faisons correspondre le paramètre de type de la fonction travarse à cette nouvelle fonction de séquence.
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 est la fonction#Puisqu'il s'agit d'identité, x et o sont la même instance
if(o.isEmpty()) {
return Optional.empty();
}
ys.add(o.get()); //Après tout, c'est la même chose que d'emballer le contenu de x
}
return Optional.of(ys);
}
Comme pour la fonction de séquence définie au début, vous pouvez voir que nous venons de supprimer le Optional de chaque élément de List <Optional <T >>
.
Je ne pense pas que Java définit des fonctions à usage général qui prennent ces fonctions comme arguments (c'est difficile à normaliser car il existe de nombreuses interfaces de type fonction), mais j'y ai pensé d'une manière ou d'une autre, alors je l'ai écrit. Mis à part son côté pratique, il est recommandé car il est amusant de faire des exercices cérébraux.
Merci d'avoir lu pour moi jusqu'à la fin. Si vous avez des questions ou des lacunes, veuillez nous contacter dans la section commentaires ou Twitter.
[^ 1]: facultatif x-> x
, mais cette façon d'écrire générera un objet de typeFunction <T, T> ʻà chaque fois, donc à moins d'avoir une raison spécifique Je pense qu'il vaut mieux utiliser
Function.identity (). [^ 3]: Cela peut prêter à confusion car il est couvert par le paramètre de type
T, mais ce que le travarse
T et la séquence
T` indiquent ici est un type différent. «T» de travarse correspond à «Optionnel
Recommended Posts