Was bedeutet Lambda mit 2 Pfeilen in Java 8?

118

Ich habe bereits mehrere Java 8-Tutorials gelesen.

Im Moment bin ich auf folgendes Thema gestoßen: Unterstützt Java Currying?

Hier sehe ich folgenden Code:

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

Ich verstehe, dass dieses Beispiel 2 Elemente summiert, aber ich kann die Konstruktion nicht verstehen:

a -> b -> a + b;

Gemäß dem linken Teil des Ausdrucks sollte diese Zeile die folgende Funktion implementieren:

R apply(int value); 

Vorher habe ich nur Lambdas mit einem Pfeil getroffen.

gstackoverflow
quelle
6
Verstehst du, was Currying bedeutet? Das ist ziemlich wichtig für diese Frage.
BlueRaja - Danny Pflughoeft
12
Es ist nur ein Lambda, das ein Lambda zurückgibt.
Ilmari Karonen

Antworten:

118

Wenn Sie dies als Nicht-Kurzschrift-Lambda-Syntax oder als anonyme Java-Klassensyntax vor Lambda ausdrücken, ist klarer, was passiert ...

Die ursprüngliche Frage. Warum sind zwei Pfeile? Einfach, es werden zwei Funktionen definiert ... Die erste Funktion ist eine funktionsdefinierende Funktion, die zweite ist das Ergebnis dieser Funktion, die zufällig auch eine Funktion ist. Für jeden muss ein ->Operator definiert werden.

Nicht Kurzschrift

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

Pre-Lambda vor 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;
    }
};
Adam
quelle
1
Pre-Lambda erfordert einfinal int value
njzk2
8
Ja, Sie haben Recht, aber ich habe geschrieben, dass immer noch ein Java 8-Compiler verwendet wird, mit dem Sie Dinge verwenden können, die "effektiv endgültig" sind
Adam
1
@gstackoverflow ja, aber dann ist Java 8 nichtpre-lambda
njzk2
1
Sie können den Pre-Lambda-Stil auch in Java 8 verwenden. Ich schrieb es JFY
gstackoverflow
3
Wurden Lambdas nicht nur gemacht, damit SO Leute Punkte sammeln konnten? :-)
Stephane
48

An IntFunction<R>ist eine Funktion int -> R. An IntUnaryOperatorist eine Funktion int -> int.

Somit IntFunction<IntUnaryOperator>ist an eine Funktion, die einen intas-Parameter verwendet und eine Funktion zurückgibt, die einen intas-Parameter verwendet und einen zurückgibt 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

Vielleicht ist es klarer, wenn Sie anonyme Klassen verwenden, um das Lambda zu "zerlegen":

IntFunction<IntUnaryOperator> add = new IntFunction<IntUnaryOperator>() {
    @Override
    public IntUnaryOperator apply(int a) {
        return new IntUnaryOperator() {
            @Override
            public int applyAsInt(int b) {
                return a + b;
            }
        };
    }
};
Alexis C.
quelle
29

Das Hinzufügen von Klammern kann dies deutlicher machen:

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

Oder wahrscheinlich kann eine Zwischenvariable helfen:

IntFunction<IntUnaryOperator> curriedAdd = a -> {
    IntUnaryOperator op = b -> a + b;
    return op;
};
Tagir Valeev
quelle
24

Schreiben wir diesen Lambda-Ausdruck in Klammern um, um es klarer zu machen:

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

Wir deklarieren also eine Funktion mit a, intdie a zurückgibt Function. Insbesondere nimmt die zurückgegebene Funktion ein intund gibt ein int(die Summe der beiden Elemente) zurück: Dies kann als dargestellt werden IntUnaryOperator.

Daher curriedAddist eine Funktion eine intund nimmt eine zurück IntUnaryOperator, so dass sie als dargestellt werden kann IntFunction<IntUnaryOperator>.

Tunaki
quelle
9

Es sind zwei Lambda-Ausdrücke.

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
ein besserer oliver
quelle
8

Wenn Sie es betrachten IntFunction, könnte es klarer werden: IntFunction<R>ist a FunctionalInterface. Es stellt eine Funktion dar, die inteinen Wert vom Typ annimmt und zurückgibt R.

In diesem Fall ist der Rückgabetyp Rauch a FunctionalInterface, nämlich an IntUnaryOperator. Die erste (äußere) Funktion selbst gibt also eine Funktion zurück.

In diesem Fall: Wenn auf ein angewendet int, curriedAddsoll eine Funktion zurückgegeben werden, die wieder ein übernimmt int(und wieder zurückgibt int, weil dies der IntUnaryOperatorFall ist).

In der funktionalen Programmierung ist es üblich, den Typ einer Funktion als zu schreiben, param -> return_valueund genau das sehen Sie hier. So ist die Art von curriedAddist int -> int -> int(oder , int -> (int -> int)wenn Sie so besser).

Die Lambda-Syntax von Java 8 geht damit einher. Um eine solche Funktion zu definieren, schreiben Sie

a -> b -> a + b

Das ist dem tatsächlichen Lambda-Kalkül sehr ähnlich:

λa λb a + b

λb a + bist eine Funktion, die einen einzelnen Parameter verwendet bund einen Wert (die Summe) zurückgibt. λa λb a + bist eine Funktion, die einen einzelnen Parameter akzeptiert und eine aandere Funktion eines einzelnen Parameters zurückgibt. λa λb a + bkehrt λb a + bmit aauf den Parameterwert gesetzt zurück.

dhke
quelle