Traitement de la liste compris par les images --java8 stream / javaslang- Article supplémentaire!
C'était trop long comme d'habitude, alors je l'ai séparé.
Allons-y grossièrement!
Il existe trois définitions de «réduire» dans le flux java8,
Je vais vous présenter les deux autres.
Le premier est celui-ci.
Optional<T> reduce(BinaryOperator<T> accumulator);
Optional<T> reduce( ((T, T) -> T) f); //Simplification
T reduce(T t, ((T, T) -> T) f); //Republier la première réduction
La différence d'avant est qu'il n'y a pas de T t
et le retour est enveloppé dans ʻOptional`.
Vous pouvez voir pourquoi cela se produit en regardant l'image.
Ceux qui ont le premier T t
Ceux qui n'ont pas T t
La différence entre la spécification de la valeur initiale par vous-même et l'utilisation de la première comme valeur initiale est la différence de présence ou d'absence de «T t».
Donc, pourquoi le retour est «Optionnel» peut être compris en considérant le cas d'une liste vide.
Si la liste est vide, T t
peut être le résultat final
Rien ne se passe s'il n'y a pas de T t
et que la liste est vide
En effet, s'il n'y a pas de valeur initiale et que la liste est vide, un retour ne peut pas être préparé.
Je vais vous présenter le reste.
La seconde est la suivante.
<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
U reduce(U u, ((U, T) -> U) f, ((U, U) -> U) g); //Simplification
U reduce(U u, ((U, T) -> U) f); //Pour le moment, oubliez le troisième argument
T reduce(T t, ((T, T) -> T) f); //Republier la première réduction
Si vous oubliez le mystérieux troisième argument, il est très similaire au premier «réduire».
Cela signifie que même si la valeur initiale est différente du type de liste, elle peut être pliée, et dans d'autres langues, cette forme est la norme.
Ainsi, comme pour les 3 derniers arguments, java8 stream considère l'exécution parallèle, il nécessite donc une méthode pour fusionner les résultats de l'exécution fractionnée. Pour être honnête, je veux que vous cachiez ça.
(C'est vraiment une "image" car je n'ai pas lu correctement le traitement interne)Il peut y avoir une certaine résistance à l'argument 3, mais si vous pensez que l'argument 3 est du bruit, c'est le même que les autres langues, il est donc bon de l'apprendre sans l'éviter.
Si vous voulez connaître le total de «[10, 30, 45, 15]», vous devez utiliser «sum».
«réduire» n'a de valeur que si le type initial est différent. Si vous pouvez utiliser un type différent pour la valeur initiale, vous pouvez le faire.
Par exemple, vous pouvez rapidement écrire le processus de vérification si la liste de (
et )
est fermée correctement avec réduire
.
resolve(asList('(', '(', ')', ')')) // true
resolve(asList('(', '(', ')')) // false
resolve(asList('(', ')', ')', '(')) // false
private static boolean resolve(List<Character> cs) {
return cs.stream()
.reduce(
Optional.of(0),
(acc, c) -> c == '('
? acc.map(n -> n + 1)
: acc.map(n -> n - 1).filter(n -> n >= 0),
(acc1, acc2) -> acc1
.flatMap(v1 -> acc2.map(v2 -> v1 + v2))
).equals(Optional.of(0));
}
Définissez la valeur initiale sur ʻOptional (0) , et pendant la convolution, définissez
.map (n + 1) sur ʻOptional (n)
pour (
et .map sur
.map (n) pour
) . Réglez sur (n -1)
. Cependant, s'il est inférieur à ʻOptional (0) , il sera" vide ". Une fois qu'il devient ʻempty
, même si vous faites ʻempty.map (n + 1) , il ne reviendra jamais à ʻOptional (0)
.
Si c'est ʻOptional (0) à la fin après le pliage, cela signifie que le nombre de
(et
) est le même, et il n'y en a jamais eu trop
) `.
Le troisième argument qui fusionne deux ʻOptionals peut être ajouté en regardant à l'intérieur si les deux sont ʻOptional (n)
. Si les deux sont ~ ~, c'est flatMap
. Cela fait du bien si cela sort bientôt.
U reduce(U u, ((U, T) -> U) f, ((U, U) -> U) g); //Republier
Apprenons en regardant le moule.
Puisque réduire doit faire attention à l'ordre et à la direction du calcul, je vais donner un exemple rapide.
Les deux ont le même résultat.
Stream.of(1, 2, 3).reduce((acc, n) -> acc + n).orElse(0) // 6
Stream.of(1, 2, 3).reduce(0, (acc, n) -> acc + n) // 6
Mais les deux résultats sont différents.
Stream.of("1", "ok", "15:10").reduce((acc, s) -> acc + " | " + s).orElse("") // 1 | ok | 15:10
Stream.of("1", "ok", "15:10").reduce("", (acc, s) -> acc + " | " + s) // | 1 | ok | 15:10
Vous pouvez comprendre la différence en dessinant vous-même une image.
Bon à retenir.
Maintenant que nous avons touché à «réduire», jetons un œil à «réduire» de javaslang.
List.of("1", "ok", "15:10").reduceRightOption((s, acc) -> acc + " | " + s).getOrElse(""); // 15:10 | ok | 1
«Réduire», qui se replie par la droite, est également disponible en fonction de la langue et de la bibliothèque. (Notez que ce n'est pas (acc, s) ->
mais (s, acc) ->
)
Bien sûr, même si vous l'utilisez, le résultat sera souvent différent du cas de gauche, alors soyez prudent.
C'est la fin de «réduire»!
Présentation de takeWhile
, qui est utile à retenir par opération de liste. Ceci est introduit en utilisant javaslang.
(Je ne savais pas que le flux java8 n'avait pas takeWhile
...)
Mettons la ligne label: millisec
du premier sujet, dans l'ordre du plus ancien, uniquement là où elle est inférieure à 30.
lines
.filter(line -> line.contains(":"))
.map(line -> Integer.valueOf(line.split(":")[1]))
.sorted()
.takeWhile(n -> n < 30); // [10, 15]
takeWhile
est comme" ne reprendre depuis le début que lorsque certaines conditions sont remplies. "
Je l'utilise beaucoup avec dropWhile
, donc il est bon de s'en souvenir aussi.
Enfin, j'en présenterai un autre, zip
, que j'utilise beaucoup de manière inattendue. Ceci est introduit en utilisant javaslang.
C'est comme associer la même partie de deux listes. Cela peut être plus facile à comprendre si vous pensez que cela ressemble à une fermeture à glissière.
List.of(1, 2, 3)
.zip(List.of('a', 'b', 'c')) // [(1, a), (2, b), (3, c)]
(`Zip` ne peut être utilisé que s'il existe un type qui peut associer différents types tels que` Tuple `.)
Autre que lorsque vous voulez coupler ʻA et
B, par exemple, si vous voulez connaître la taille de chaque espace dans la liste de ʻInteger
, essayez zip
en décalant la même liste de un. Il y a un moyen.
List<Integer> times = List.of(10, 15, 35, 60); //cette[15 - 10, 35 - 15, 60 - 35]Vouloir
times
.zip(times.subSequence(1))
.map(t -> t._2 - t._1); // [5, 20, 25]
Utilisez-vous «réduire» lorsque vous pensez au traitement depuis le début? Vous pourriez penser, mais si vous faites une image, c'est complètement différent.
(Réduire
ne calcule pas avec le voisin, mais calcule la somme (ou quelque chose) jusqu'à ce point un par un, de sorte que le calcul de l'écart ne peut pas être simple.)
Si vous faites bien filtrer et mapper
sur un fichier journal, zip
, prenez la différence de temps de traitement pour chaque ligne, et essayez tri inversé et takeWhile
, les lignes qui prennent 500 ms ou plus sont affichées par ordre de lenteur Tu peux le faire.
Il y aura certainement des discussions telles que "Je ne connais pas les mérites" et "N'est-ce pas d'accord pour?"
Il y en a quelques-uns, mais j'en énumérerai brièvement trois.
Par exemple, il existe une fonction telle que String-> Integer
.
private static Integer toIntAndTwice(String s) {
return Integer.valueOf(s) * 2;
}
Si vous écrivez le code qui applique cette fonction à "quand il y a plusieurs String
s", "quand il y a un maximum d'une" chaîne "", et "quand il y a une" chaîne "qui peut être malformée", Devenir.
Exemple de liste
List<Integer> result = List.empty();
List<String> org = ...; // List.of(1, 2, 3) or empty
for (String x : org) {
result.append(toIntAndTwice(x));
}
return result;
Exemple de ʻOption`
Option<Integer> result;
Option<String> org = ...; // Option.of(1) or empty
if (org.isEmpty()) {
result = Option.none();
} else {
result = Option.of(toIntAndTwice(org.get()));
}
return result;
Exemple try
Integer result;
String org = ...; // "1" or "x"
try {
result = toIntAndTwice(org);
} catch (Throwable t) {
result = null;
}
return result;
Vous devez écrire un code complètement différent pour appliquer toIntAndTwice
à String
dans une situation particulière.
Si vous écrivez ceci avec map
, ce sera comme ça.
Exemple de liste
List<String> org = ...;
List<Integer> mapped = org.map(JSMain::toIntAndTwice);
return mapped;
Exemple de ʻOption`
Option<String> org = ...;
Option<Integer> mapped = org.map(JSMain::toIntAndTwice);
return mapped;
Exemple Try
Try<String> org = ...;
Try<Integer> mapped = org.map(JSMain::toIntAndTwice);
return mapped;
C'est pareil!
Ceci est dû au fait que "les règles de List
et de ʻOption dans une situation spécifique" et "ce que vous voulez réellement faire (" toIntAndTwice
) "sont séparées, et la première est suivie par la langue.
Au fait, si le code est similaire jusqu'à présent, je pense qu'il peut être rendu plus courant, non?
Puisque List
et ʻOption de javaslang héritent de
Value`, vous pouvez également le faire.
Si vous définissez une telle Valeur <T> -> Valeur <R>
,
private static Value<Integer> mapAnyType(Value<String> org) {
return org.map(JSMain::toIntAndTwice);
}
Cela fonctionne que l'argument soit List
ou ʻOption`!
Exemple de liste
Value<Integer> m1 = mapAnyType(List.of("1", "2", "3")); // List(2, 4, 6)
Exemple de ʻOption`
Value<Integer> m2 = mapAnyType(Option.none()); // None
Exemple Try
Value<Integer> m3 = mapAnyType(Try.of(() -> "x")); // Failure(java.lang.NumberFormatException: For input string: "x")
Dans ce cas, le même code peut gérer différentes situations, donc par exemple, préparer un processus d'annulation pour les options payantes
, et avoir une" fonction pour tout annuler à la fois (Liste
)" et une "fonction pour annuler si vous en avez une (ʻOption". ")" Et "La fonction à annuler parce que vous devriez l'avoir ( Try
) "peut être réalisée à la fois.
Je pense que le plus grand mérite est la séparation de la «situation» et du «traitement».
Un code hérité que tout le monde aime. Ceci est courant.
//Initialisation
result = 0;
flag = false;
for (i ...) {
result = ...;
//Est-ce que ~ ~ est terminé?
if (i < ...) {
result = ...;
flag = true;
}
//Si c'est ~~, ça se termine
if (flag) {
return result;
}
//Puis boucle avec ~ ~
for (j ...) {
result = ...;
//Si c'est ~~, ça se termine
if (j < ...) {
return result;
}
}
//Initialisation
flag = false;
}
//revenir
return result;
Le return result;
dans ce code est le même que le texte, mais le contenu est complètement différent. (Peut-être, je ne sais même pas.)
Puisqu'il y a un contexte dans les lignes, vous ne pouvez pas copier et coller uniquement cette ligne, et il semble que vous créez des blocs avec des commentaires, mais en réalité ce n'est qu'un gros bloc.
Si cela ressemble au code ci-dessus, les trois lignes de l'argument fonctionnent indépendamment et il n'y a pas de variables en cours dans la portée de la méthode qui ne devraient pas être «return».
(Puisqu'il y en a un ;
, ce code est une ligne. Par conséquent, il ne peut y avoir aucun état d'intervalle.)
return cs.stream()
.reduce(
Optional.of(0),
(acc, c) -> c == '(' ? acc.map(n -> n + 1) : acc.map(n -> n - 1).filter(n -> n >= 0),
(acc1, acc2) -> acc1.flatMap(v1 -> acc2.map(v2 -> v1 + v2))
).equals(Optional.of(0));
Je pense que c'est extrêmement réutilisable et de meilleure qualité.
(Bien sûr, si vous le faites avec du code partagé, vous devriez être un peu plus prudent. Le deuxième argument doit être nommé correctement et le code de test doit être écrit légèrement. Le troisième argument doit être ReduceUtil :: mergeOptional
. Java efficace Je pense que la 3e édition l'a également dit.)
Je reviendrai ensuite sur les détails, mais plus vous connaissez l'idée elle-même, mieux ce sera à première vue à moins qu'il ne s'agisse d'un langage spécial.
Si vous apprenez le flux java8 et passez à ruby, vous pourrez utiliser la liste immédiatement, et même si vous êtes nouveau sur java8, si vous êtes expérimenté avec python, vous pourrez diffuser.
Comme mentionné dans les avantages de dernière minute, la plupart des langues ont mapper, filtrer, réduire
.
Lorsque vous devez maintenir une langue que vous ne connaissez pas à la hâte au travail, ou lorsque vous souhaitez apporter des modifications aux outils que vous avez choisis, c'est bien si vous connaissez les mots et le traitement des images. Donc, à la fin, je terminerai par un résumé de la façon de faire le même traitement dans la langue que vous entendez souvent.
(La partie avec (*)
donne l'impression que vous pouvez faire quelque chose de similaire si vous l'utilisez bien)
lang | map | filter | reduce(zero / left) reduce(head / left) reduce(zero / right) reduce(head / right) |
take while | zip |
---|---|---|---|---|---|
java | map | filter | reduce reduce - - |
- | - |
groovy | collect | findAll | inject inject - - |
takeWhile | transpose (*) |
scala | map | filter | reduceLeft / reduceLeftOption foldLeft reduceRight / reduceRightOption foldRight |
takeWhile | zip |
python | map | filter | reduce - - - |
itertools.takewhile | zip |
php | array_map | array_filter | array_reduce array_reduce - - |
- | array_map (*) |
ruby | map / collect | select | reduce / inject reduce / inject - - |
take_while | zip |
js | map | filter | reduce reduce reduceRight reduceRight |
- | - |
haskell | map | filter | foldl foldl1 foldr foldr1 |
takeWhile | zip |
Si vous vous souvenez des mots autour de map / collect
, filter
, reduction / fold / inject
, la plupart des langages fonctionneront.
«map vs collect» et «reduction vs inject» peuvent être intéressants à examiner.
Si quelqu'un a lu jusqu'ici, merci.
C'était bien que l'article soit à l'heure le jour où je l'ai déclaré. C'est tout.
Recommended Posts