Apa arti lambda dengan 2 panah di Java 8?

118

Saya telah membaca beberapa tutorial Java 8 sebelumnya.

Saat ini saya menemukan topik berikut: Apakah java mendukung kari?

Di sini, saya melihat kode berikut:

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

Saya mengerti bahwa contoh ini menjumlahkan 2 elemen tetapi saya tidak dapat memahami konstruksinya:

a -> b -> a + b;

Menurut bagian kiri ekspresi, baris ini harus mengimplementasikan fungsi berikut:

R apply(int value); 

Sebelumnya, saya hanya bertemu lambda hanya dengan satu anak panah.

gstackoverflow
sumber
6
Apa kamu paham apa arti kari? Itu sangat integral dengan pertanyaan ini.
BlueRaja - Danny Pflughoeft
12
Itu hanya lambda yang mengembalikan lambda.
Ilmari Karonen

Jawaban:

118

Jika Anda mengekspresikan ini sebagai sintaks lambda non-singkatan atau sintaks kelas anonim Java pre-lambda, lebih jelas apa yang terjadi ...

Pertanyaan asli. Mengapa dua anak panah? Sederhana, ada dua fungsi yang didefinisikan ... Fungsi pertama adalah fungsi yang menentukan fungsi, yang kedua adalah hasil dari fungsi itu, yang juga kebetulan merupakan fungsi. Masing-masing membutuhkan ->operator untuk mendefinisikannya.

Non-steno

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

Pra-Lambda sebelum 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
sumber
1
pre-lambda membutuhkanfinal int value
njzk2
8
Ya, Anda benar, tetapi saya menulis bahwa masih menggunakan compiler Java 8 yang memungkinkan Anda untuk menggunakan hal-hal yang "final secara efektif"
Adam
1
@gstackoverflow ya, tapi kemudian java 8 tidakpre-lambda
njzk2
1
Anda juga bisa menggunakan gaya pre-lambda di java 8. Saya menulisnya JFY
gstackoverflow
3
Bukankah lambda dilakukan begitu saja agar orang-orang bisa mencetak poin? :-)
Stephane
48

An IntFunction<R>adalah sebuah fungsi int -> R. An IntUnaryOperatoradalah sebuah fungsi int -> int.

Jadi an IntFunction<IntUnaryOperator>adalah fungsi yang mengambil intsebagai parameter dan mengembalikan fungsi yang mengambil intsebagai parameter dan mengembalikan 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

Mungkin akan lebih jelas jika Anda menggunakan kelas anonim untuk "mendekomposisi" 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;
            }
        };
    }
};
Alexis C.
sumber
29

Menambahkan tanda kurung mungkin membuat ini lebih jelas:

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

Atau mungkin variabel perantara dapat membantu:

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

Mari kita tulis ulang ekspresi lambda itu dengan tanda kurung agar lebih jelas:

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

Jadi kami mendeklarasikan fungsi yang mengambil intyang mengembalikan a Function. Lebih khusus lagi, fungsi yang dikembalikan mengambil intdan mengembalikan int(jumlah dari dua elemen): ini bisa direpresentasikan sebagai IntUnaryOperator.

Oleh karena itu, curriedAddadalah fungsi yang mengambil intdan mengembalikan IntUnaryOperator, sehingga dapat direpresentasikan sebagai IntFunction<IntUnaryOperator>.

Tunaki
sumber
9

Itu dua ekspresi 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
oliver yang lebih baik
sumber
8

Jika Anda melihat IntFunctionmungkin menjadi lebih jelas: IntFunction<R>adalah a FunctionalInterface. Ini mewakili fungsi yang mengambil intdan mengembalikan nilai tipe R.

Dalam hal ini, tipe kembaliannya Rjuga a FunctionalInterface, yaitu IntUnaryOperator. Jadi yang pertama (luar) itu sendiri mengembalikan fungsi.

Dalam hal ini: Ketika diterapkan ke int, curriedAdddiharapkan untuk mengembalikan fungsi yang lagi mengambil int(dan kembali lagi int, karena itulah IntUnaryOperator).

Dalam pemrograman fungsional, adalah umum untuk menulis jenis fungsi sebagai param -> return_valuedan Anda melihatnya persis di sini. Jadi jenisnya curriedAddadalah int -> int -> int(atau int -> (int -> int)jika Anda menyukainya lebih baik).

Sintaks lambda Java 8 sejalan dengan ini. Untuk mendefinisikan fungsi seperti itu, Anda menulis

a -> b -> a + b

yang sangat mirip dengan kalkulus lambda sebenarnya:

λa λb a + b

λb a + badalah fungsi yang mengambil satu parameter bdan mengembalikan nilai (jumlah). λa λb a + badalah fungsi yang menerima satu parameter adan mengembalikan fungsi lain dari satu parameter. λa λb a + bkembali λb a + bdengan aset ke nilai parameter.

dhke
sumber