Je vois java.util.function.BiFunction, donc je peux faire ceci:
BiFunction<Integer, Integer, Integer> f = (x, y) -> { return 0; };
Et si cela ne suffit pas et que j'ai besoin de TriFunction? Ça n'existe pas!
TriFunction<Integer, Integer, Integer, Integer> f = (x, y, z) -> { return 0; };
Je suppose que je devrais ajouter que je sais que je peux définir ma propre TriFunction, j'essaie juste de comprendre la raison de ne pas l'inclure dans la bibliothèque standard.
Réponses:
Autant que je sache, il n'y a que deux types de fonctions, destructives et constructives.
Alors que la fonction constructive, comme son nom l'indique, construit quelque chose, une fonction destructrice détruit quelque chose, mais pas de la manière que vous pensez actuellement.
Par exemple, la fonction
est une question constructive . Comme vous avez besoin de construire quelque chose. Dans l'exemple, vous avez construit le tuple (x, y) . Les fonctions constructives ont le problème de ne pas pouvoir gérer des arguments infinis. Mais le pire, c'est que vous ne pouvez pas simplement laisser une dispute ouverte. Vous ne pouvez pas simplement dire "eh bien, laissez x: = 1" et essayer chaque y que vous voudrez peut-être essayer. Vous devez construire à chaque fois le tuple entier avec
x := 1
. Donc, si vous aimez voir ce que les fonctions renvoient,y := 1, y := 2, y := 3
vous devez écriref(1,1) , f(1,2) , f(1,3)
.Dans Java 8, les fonctions constructives doivent être gérées (la plupart du temps) en utilisant des références de méthode car il n'y a pas beaucoup d'avantages à utiliser une fonction lambda constructive. Ce sont un peu comme des méthodes statiques. Vous pouvez les utiliser, mais ils n'ont aucun état réel.
L'autre type est le destructeur, il prend quelque chose et le démonte autant que nécessaire. Par exemple, la fonction destructive
fait la même chose que la fonction
f
qui était constructive. Les avantages d'une fonction destructive sont que vous pouvez désormais gérer des arguments infinis, ce qui est particulièrement pratique pour les flux, et vous pouvez simplement laisser les arguments ouverts. Donc, si vous voulez à nouveau voir à quoi ressemblerait le résultat six := 1
ety := 1 , y := 2 , y := 3
, vous pouvez direh = g(1)
eth(1)
est le résultat poury := 1
,h(2)
poury := 2
eth(3)
poury := 3
.Vous avez donc ici un état fixe! C'est assez dynamique et c'est la plupart du temps que ce que l'on attend d'un lambda.
Les modèles comme Factory sont beaucoup plus faciles si vous pouvez simplement ajouter une fonction qui fait le travail pour vous.
Les destructeurs se combinent facilement les uns avec les autres. Si le type est correct, vous pouvez simplement les composer comme vous le souhaitez. En utilisant cela, vous pouvez facilement définir des morphismes qui facilitent (avec des valeurs immuables) les tests!
Vous pouvez le faire aussi avec une composition constructive, mais la composition destructive ressemble plus à une liste ou à un décorateur, et la composition constructive ressemble beaucoup à un arbre. Et des choses comme le retour en arrière avec des fonctions constructives ne sont tout simplement pas agréables. Vous pouvez simplement sauvegarder les fonctions partielles d'une fonction destructive (programmation dynamique), et en "retour arrière", utilisez simplement l'ancienne fonction destructive. Cela rend le code beaucoup plus petit et plus lisible. Avec les fonctions constructives, vous avez plus ou moins à vous souvenir de tous les arguments, ce qui peut être beaucoup.
Alors, pourquoi y a-t-il un besoin de
BiFunction
devrait être plus question que pourquoi il n'y en a pasTriFunction
?Tout d'abord, la plupart du temps, vous n'avez que quelques valeurs (moins de 3) et n'avez besoin que d'un résultat, donc une fonction destructive normale ne serait pas du tout nécessaire, une fonction constructive ferait bien l'affaire. Et il y a des choses comme les monades qui ont vraiment besoin d'une fonction constructive. Mais à part ça, il n'y a pas vraiment beaucoup de bonnes raisons pour lesquelles il y en a
BiFunction
du tout. Ce qui ne veut pas dire qu'il devrait être supprimé! Je me bats pour mes Monades jusqu'à ma mort!Donc, si vous avez beaucoup d'arguments, que vous ne pouvez pas combiner dans une classe de conteneur logique, et si vous avez besoin que la fonction soit constructive, utilisez une référence de méthode. Sinon, essayez d'utiliser la nouvelle capacité acquise des fonctions destructrices, vous pourriez vous retrouver à faire beaucoup de choses avec beaucoup moins de lignes de code.
la source
BiFunction
été créé pour permettre une réduction aisée des données, et la plupartStream
des opérations de terminal ne sont que des réductions de données. Un bon exemple estBinaryOperator<T>
, utilisé dans beaucoupCollectors
. Un premier élément est réduit avec le second, puis peut être réduit avec le suivant, et ainsi de suite. Bien sûr, vous pouvez créer unFunction<T, Function<T, T>
func = x -> (y -> / * code de réduction ici * /). Mais sérieusement? Tout cela quand vous pouvez le faire simplementBinaryOperator<T> func = (x, y) -> /*reduction code here*/
. De plus, cette approche de réduction des données ressemble beaucoup à votre approche «destructive» pour moi.Function<Integer,Integer> f = (x,y) -> x + y
Java est valide, ce qui n'est pas le cas. Cela devrait être une BiFonction pour commencer!Si vous avez besoin de TriFunction, procédez comme suit:
Le petit programme suivant montre comment il peut être utilisé. N'oubliez pas que le type de résultat est spécifié comme dernier paramètre de type générique.
Je suppose que s'il y avait une utilisation pratique de TriFunction dans
java.util.*
oujava.lang.*
il aurait été défini. Je n'irais jamais au-delà de 22 arguments, cependant ;-) Ce que je veux dire par là, tout nouveau code qui permet de diffuser des collections n'a jamais eu besoin de TriFunction comme l'un des paramètres de la méthode. Cela n'a donc pas été inclus.METTRE À JOUR
Pour être complet et en suivant l'explication des fonctions destructives dans une autre réponse (liée au currying), voici comment TriFunction peut être émulée sans interface supplémentaire:
Bien entendu, il est possible de combiner des fonctions d'autres manières, par exemple:
Alors que le curry serait naturel pour tout langage qui prend en charge la programmation fonctionnelle au-delà des lambdas, Java n'est pas construit de cette façon et, bien que réalisable, le code est difficile à maintenir, et parfois lu. Cependant, il est très utile comme exercice, et parfois des fonctions partielles ont une place légitime dans votre code.
la source
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
Voir stackoverflow.com/questions/19834611/…andThen()
Exemple d'utilisation ajouté à la réponse.BiFunction
est utilisé dans l'Stream
API pour effectuer une réduction des données, ce qui ressemble beaucoup à l'approche du curry pour moi: vous n'en prenez jamais plus de deux arguments, et vous pouvez traiter n'importe quel nombre d'éléments, une réduction à la fois (voir mon commentaire sur la réponse acceptée, je serais heureux de savoir si je me trompe de voir les choses de cette façon).L'alternative est, ajoutez la dépendance ci-dessous,
Maintenant, vous pouvez utiliser la fonction Vavr, comme ci-dessous jusqu'à 8 arguments,
3 arguments:
5 arguments:
la source
vavr
bibliothèque - cela rend la programmation de style fonctionnel aussi supportable que possible en Java.J'ai presque la même question et une réponse partielle. Je ne sais pas si la réponse constructive / déconstructive est ce que les concepteurs de langage avaient en tête. Je pense qu'avoir 3 et plus jusqu'à N a des cas d'utilisation valides.
Je viens de .NET. et dans .NET vous avez Func et Action pour les fonctions void. Prédicat et quelques autres cas spéciaux existent également. Voir: https://msdn.microsoft.com/en-us/library/bb534960(v=vs.110).aspx
Je me demande quelle est la raison pour laquelle les concepteurs de langage ont opté pour Function, Bifunction et n'ont pas continué jusqu'à DecaExiFunction?
La réponse à la deuxième partie est l'effacement de type. Après compilation, il n'y a aucune différence entre Func et Func. Ce qui suit ne compile donc pas:
Des fonctions internes ont été utilisées pour contourner un autre problème mineur. Eclipse a insisté pour avoir les deux classes dans des fichiers nommés Function dans le même répertoire ... Je ne sais pas si c'est un problème de compilateur de nos jours. Mais je ne peux pas transformer l'erreur de dans Eclipse.
Func a été utilisé pour éviter les conflits de noms avec le type de fonction java.
Donc, si vous voulez ajouter Func de 3 à 16 arguments, vous pouvez faire deux choses.
Exemple pour la deuxième manière:
et
Quelle serait la meilleure approche?
Dans les exemples ci-dessus, je n'ai pas inclus d'implémentations pour les méthodes andThen () et compose (). Si vous ajoutez ces derniers, vous devez ajouter 16 surcharges chacun: le TriFunc doit avoir un andthen () avec 16 arguments. Cela vous donnerait une erreur de compilation en raison des dépendances circulaires. De plus, vous n'auriez pas ces surcharges pour Function et BiFunction. Par conséquent, vous devez également définir Func avec un argument et Func avec deux arguments. Dans .NET, les dépendances circulaires seraient contournées en utilisant des méthodes d'extension qui ne sont pas présentes en Java.
la source
andThen
de 16 arguments? Le résultat d'une fonction en Java est une valeur unique.andThen
prend cette valeur et en fait quelque chose. De plus, il n'y a pas de problème de dénomination. Les noms de classe doivent être différents et se trouver dans des fichiers différents nommés de la même manière - en suivant la logique définie par les développeurs de langage Java avec Function et BiFunction. De plus, tous ces noms différents sont nécessaires si les types d'arguments sont différents. On peut créer unVargFunction(T, R) { R apply(T.. t) ... }
seul type.J'ai trouvé le code source de BiFunction ici:
https://github.com/JetBrains/jdk8u_jdk/blob/master/src/share/classes/java/util/function/BiFunction.java
Je l'ai modifié pour créer TriFunction. Comme BiFunction, il utilise andThen () et non compose (), donc pour certaines applications qui nécessitent compose (), cela peut ne pas être approprié. Cela devrait convenir aux types d'objets normaux. Un bon article sur andThen () et compose () peut être trouvé ici:
http://www.deadcoderising.com/2015-09-07-java-8-functional-composition-using-compose-and-andthen/
la source
Vous pouvez également créer votre propre fonction en prenant les 3 paramètres
la source
Vous ne pouvez pas toujours vous arrêter à TriFunction. Parfois, vous devrez peut-être passer n nombre de paramètres à vos fonctions. Ensuite, l'équipe de support devra créer une QuadFunction pour corriger votre code. La solution à long terme serait de créer un objet avec les paramètres supplémentaires, puis d'utiliser la fonction ou la biFonction prête à l'emploi.
la source