Cosa significa lambda con 2 frecce in Java 8?


118

Ho letto diversi tutorial su Java 8 prima.

In questo momento ho riscontrato il seguente argomento: Java supporta il curry?

Qui vedo il seguente codice:

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

Capisco che questo esempio somma 2 elementi ma non riesco a capire la costruzione:

a -> b -> a + b;

Secondo la parte sinistra dell'espressione, questa riga dovrebbe implementare la seguente funzione:

R apply(int value); 

Prima di questo, ho incontrato solo lambda con una freccia.


6
Capisci cosa significa curry? È abbastanza parte integrante di questa domanda.
BlueRaja - Danny Pflughoeft

12
È solo un lambda che restituisce un lambda.
Ilmari Karonen

Risposte:


118

Se lo esprimi come sintassi lambda non abbreviata o sintassi della classe anonima Java pre-lambda, è più chiaro cosa sta succedendo ...

La domanda originale. Perché sono due frecce? Semplice, ci sono due funzioni in fase di definizione ... La prima funzione è una funzione che definisce, la seconda è il risultato di quella funzione, che è anche funzione. Ciascuno richiede un ->operatore per definirlo.

Non-stenografia

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

Pre-Lambda prima di 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;
    }
};

1
pre-lambda richiede unfinal int value
njzk2

8
Sì, hai ragione, ma l'ho scritto usando ancora un compilatore Java 8 che ti permette di usare cose che sono "effettivamente definitive"
Adam

1
@gstackoverflow sì, ma allora java 8 non lo èpre-lambda
njzk2

1
puoi usare lo stile pre-lambda anche in java 8. L'ho scritto JFY
gstackoverflow il

3
I lambda non sono stati fatti solo per COSÌ la gente poteva segnare punti? :-)
Stephane

48

An IntFunction<R>è una funzione int -> R. An IntUnaryOperatorè una funzione int -> int.

Quindi an IntFunction<IntUnaryOperator>è una funzione che accetta un intparametro e restituisce una funzione che accetta un intparametro e restituisce 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

Forse è più chiaro se usi classi anonime per "scomporre" la 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;
            }
        };
    }
};

29

L'aggiunta di parentesi può renderlo più chiaro:

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

O probabilmente la variabile intermedia può aiutare:

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

24

Riscriviamo quell'espressione lambda con parentesi per renderla più chiara:

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

Quindi stiamo dichiarando una funzione che prende un intche restituisce a Function. Più specificamente, la funzione restituita prende un inte restituisce un int(la somma dei due elementi): questo può essere rappresentato come unIntUnaryOperator .

Pertanto, curriedAddè una funzione che prende inte restituisce un IntUnaryOperator, quindi può essere rappresentata come IntFunction<IntUnaryOperator>.


9

Sono due espressioni 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

8

Se guardi IntFunctionpotrebbe diventare più chiaro: IntFunction<R>è un file FunctionalInterface. Rappresenta una funzione che accetta inte restituisce un valore di tipo R.

In questo caso, anche il tipo restituito Rè a FunctionalInterface, ovvero un file IntUnaryOperator. Quindi il primo (esterna) stessa restituisce una funzione.

In questo caso: quando applicato a un int, curriedAdddovrebbe restituire una funzione che accetta di nuovo un int(e ritorna di nuovo int, perché è quello che IntUnaryOperatorfa).

Nella programmazione funzionale è comune scrivere il tipo di una funzione as param -> return_valuee qui si vede esattamente questo. Quindi il tipo di curriedAddè int -> int -> int(o int -> (int -> int)se ti piace di più).

La sintassi lambda di Java 8 va di pari passo con questo. Per definire una tale funzione, scrivi

a -> b -> a + b

che è molto simile al lambda calcolo effettivo:

λa λb a + b

λb a + bè una funzione che accetta un singolo parametro be restituisce un valore (la somma). λa λb a + bè una funzione che accetta un singolo parametro ae restituisce un'altra funzione di un singolo parametro. λa λb a + britorna λb a + bcon aset al valore del parametro.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.