Que signifie lambda avec 2 flèches dans Java 8?

J'ai déjà lu plusieurs tutoriels Java 8.

En ce moment, j'ai rencontré le sujet suivant: java prend-il en charge le Currying?

Ici, je vois le code suivant:

IntFunction<IntUnaryOperator> curriedAdd = a -> b -> a + b;
System.out.println(curriedAdd.apply(1).applyAsInt(12));

Je comprends que cet exemple somme 2 éléments mais je ne peux pas comprendre la construction:

a -> b -> a + b;

Selon la partie gauche de l'expression, cette ligne devrait implémenter la fonction suivante:

R apply(int value); 

Avant cela, je n'ai rencontré lambdas qu'avec une seule flèche.

113
demandé sur Community 2015-09-28 13:23:57

6 réponses

Si vous exprimez cela comme une syntaxe lambda non abrégée ou une syntaxe de classe anonyme Java pré-lambda, ce qui se passe est plus clair...

La question initiale. Pourquoi sont deux flèches? Simple, il y a deux fonctions en cours de définition... La première fonction est une fonction définissant la fonction, la seconde est le résultat de cette fonction, qui se trouve également être une fonction. Chacun nécessite un opérateur -> pour le définir.

Non-sténographie

IntFunction<IntUnaryOperator> curriedAdd = (a) -> {
    return (b) -> {
        return a + b;
    };
};

Pré-Lambda avant Java 8

IntFunction<IntUnaryOperator> curriedAdd = new IntFunction<IntUnaryOperator>() {
    @Override
    public IntUnaryOperator apply(final int value) {
        IntUnaryOperator op = new IntUnaryOperator() {
            @Override
            public int applyAsInt(int operand) {
                return operand + value;
            }
        };
        return op;
    }
};
115
répondu Adam 2015-09-30 16:10:23

Un IntFunction<R> est une fonction de int -> R. Un IntUnaryOperator est une fonction int -> int.

Donc IntFunction<IntUnaryOperator> est une fonction qui prend un int comme paramètre et retourne une fonction qui prend un int comme paramètre et retourne un int.

a -> b -> a + b;
^    |         |
|     ---------
|         ^
|         |
|         The IntUnaryOperator (that takes an int, b) and return an int (the sum of a and b)
|
The parameter you give to the IntFunction

Peut-être est-il plus clair si vous utilisez des classes anonymes pour "décomposer" le lambda:

IntFunction<IntUnaryOperator> add = new IntFunction<IntUnaryOperator>() {
    @Override
    public IntUnaryOperator apply(int a) {
        return new IntUnaryOperator() {
            @Override
            public int applyAsInt(int b) {
                return a + b;
            }
        };
    }
};
45
répondu Alexis C. 2015-09-28 10:35:14

Ajouter des parenthèses peut rendre cela plus clair:

IntFunction<IntUnaryOperator> curriedAdd = a -> (b -> (a + b));

Ou probablement variable intermédiaire peut aider:

IntFunction<IntUnaryOperator> curriedAdd = a -> {
    IntUnaryOperator op = b -> a + b;
    return op;
};
27
répondu Tagir Valeev 2015-09-28 10:38:03

Réécrivons cette expression lambda avec des parenthèses pour la rendre plus claire:

IntFunction<IntUnaryOperator> curriedAdd = a -> (b -> (a + b));

Nous déclarons donc une fonction prenant un int qui renvoie un Function. Plus précisément, la fonction renvoyée prend un int et renvoie un int (la somme des deux éléments): ceci peut être représenté par un IntUnaryOperator.

Par conséquent, curriedAdd est une fonction prenant un int et retournant un IntUnaryOperator, donc elle peut être représentée comme IntFunction<IntUnaryOperator>.

22
répondu Tunaki 2015-09-28 10:46:44

C'est deux expressions lambda.

IntFunction<IntUnaryOperator> curriedAdd = 
  a -> { //this is for the fixed value
    return b -> { //this is for the add operation
      return a + b;
    };
  }

IntUnaryOperator addTwo = curriedAdd.apply(2);
System.out.println(addTwo.applyAsInt(12)); //prints 14
7
répondu a better oliver 2015-09-28 10:45:43

Si vous regardez IntFunction il pourrait être plus clair: IntFunction<R> est un FunctionalInterface. Il représente une fonction qui prend un int et renvoie une valeur de type R.

Dans ce cas, le type de retour R est aussi un FunctionalInterface, soit IntUnaryOperator. Ainsi, la première fonction (externe) renvoie elle-même une fonction.

Dans ce cas: Lorsqu'il est appliqué à une int, curriedAdd est censé renvoyer une fonction qui prend encore un int (et retourne de nouveau int, parce que c'est ce IntUnaryOperator faire).

En programmation fonctionnelle, il est courant d'écrire le type d'une fonction comme param -> return_value et vous voyez exactement cela ici. Donc, le type de curriedAdd est int -> int -> int (ou int -> (int -> int) si vous l'aimez mieux).

La syntaxe lambda de Java 8 va de pair avec cela. Pour définir une telle fonction, vous écrivez

a -> b -> a + b

Qui est très similaire au calcul lambda réel:

λa λb a + b

λb a + b est une fonction qui prend un seul paramètre b et retourne une valeur (de la somme). λa λb a + b est un fonction qui accepte un seul paramètre a et renvoie une autre fonction d'un seul paramètre. λa λb a + b renvoie λb a + b avec a défini sur la valeur du paramètre.

6
répondu dhke 2015-09-29 15:28:01