Java 8で2つの矢印の付いたラムダは何を意味しますか?


118

以前にいくつかのJava 8チュートリアルを読んだことがあります。

今、私は次のトピックに遭遇しました: Javaはカリー化をサポートしていますか?

ここでは、次のコードが表示されます。

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

この例では2つの要素を合計することを理解していますが、構造を理解できません。

a -> b -> a + b;

式の左側によると、この行は次の関数を実装する必要があります。

R apply(int value); 

これの前に、私は1つの矢だけでラムダに会いました。


6
カレーの意味がわかりますか?これは、この質問にかなり不可欠です。
BlueRaja-ダニープフルフフト2015

12
ラムダを返すのは単なるラムダです。
Ilmari Karonen

回答:


118

これを非短縮ラムダ構文またはプレラムダJava匿名クラス構文として表現すると、何が起こっているのかが明確になります...

元の質問。なぜ2つの矢印があるのですか?単純です、2つの関数が定義されています...最初の関数は関数定義関数です。2番目はその関数の結果であり、これもたまたま関数です。それぞれ、->オペレーターがそれを定義する必要があります。

省略形ではない

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

Java 8より前のPre-Lambda

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にはaが必要ですfinal int value
njzk2 '29 / 09/29

8
はい、あなたは正しいですが、「事実上最終的な」ものを使用できるようにするJava 8コンパイラをまだ使用していると書いています
Adam

1
@gstackoverflowはい、しかしJava 8はそうではありませんpre-lambda
njzk2

1
Java 8でもpre-lambdaスタイルを使用できます。私はJFYそれを書いた
gstackoverflow

3
ラムダは、SOの人々がポイントを獲得できるように行われたのではありませんか?:-)
ステファン

48

アンはIntFunction<R>関数ですint -> R。アンはIntUnaryOperator関数ですint -> int

したがってIntFunction<IntUnaryOperator>とる関数でありint、パラメータとして取ると関数戻りintパラメータとして、およびリターン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

ラムダを「分解」するために匿名クラスを使用する方がより明確かもしれません。

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

括弧を追加すると、これがより明確になる場合があります。

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

またはおそらく中間変数が役立つかもしれません:

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

24

そのラムダ式をかっこで書き換えて、わかりやすくします。

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

したがって、intを返すan を取る関数を宣言していますFunction。より具体的には、返される関数はを受け取り、(2つの要素の合計)をint返しますint。これはとして表すことができますIntUnaryOperator

したがって、curriedAddはを取り、intを返す関数なので、IntUnaryOperatorとして表すことができますIntFunction<IntUnaryOperator>


9

2つのラムダ式です。

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

見てみるIntFunctionと明らかになるかもしれません:IntFunction<R>ですFunctionalInterface。これは、を受け取り、inttypeの値を返す関数を表しますR

この場合、戻り値の型RFunctionalInterface、つまりIntUnaryOperatorです。したがって、最初の(外部)関数自体が関数を返します。

この場合:に適用された場合intcurriedAdd再び取る関数を返すことになっているint(再び戻りをintその者のどのようなので、IntUnaryOperatorありません)。

関数型プログラミングでは、関数の型を次のように記述するのが一般的であり、param -> return_valueここでそれを正確に確認できます。つまり、のタイプcurriedAddint -> int -> int(またはint -> (int -> int)、それが好きなら)です。

Java 8のラムダ構文はこれに対応しています。このような関数を定義するには、次のように書きます。

a -> b -> a + b

これは実際のラムダ計算によく似ています。

λa λb a + b

λb a + b単一のパラメーターを取りb、値(合計)を返す関数です。λa λb a + bは、単一のパラメーターを受け入れ、単一のパラメーターのa別の関数を返す関数です。λa λb a + bリターンλb a + baパラメータ値に設定。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.