Lambda với 2 mũi tên có nghĩa là gì trong Java 8?


118

Tôi đã đọc một số hướng dẫn về Java 8 trước đây.

Ngay bây giờ tôi gặp chủ đề sau: Java có hỗ trợ Currying không?

Ở đây, tôi thấy mã sau:

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

Tôi hiểu rằng ví dụ này tổng hợp 2 phần tử nhưng tôi không thể hiểu cấu trúc:

a -> b -> a + b;

Theo phần bên trái của biểu thức, hàng này sẽ triển khai hàm sau:

R apply(int value); 

Trước đó, tôi chỉ gặp những con lambdas chỉ với một mũi tên.


6
Bạn có hiểu cà ri nghĩa là gì không? Đó là một phần không thể thiếu cho câu hỏi này.
BlueRaja - Danny Pflughoeft,

12
Nó chỉ là một lambda trả về một lambda.
Ilmari Karonen

Câu trả lời:


118

Nếu bạn diễn đạt điều này dưới dạng cú pháp lambda không viết tắt hoặc cú pháp lớp ẩn danh trước lambda Java thì rõ ràng hơn điều gì đang xảy ra ...

Câu hỏi ban đầu. Tại sao lại là hai mũi tên? Đơn giản, có hai chức năng đang được định nghĩa ... Chức năng đầu tiên là một chức năng-xác định-chức năng, chức năng thứ hai là kết quả của chức năng đó, cũng xảy ra là chức năng. Mỗi yêu cầu một ->toán tử để xác định nó.

Không viết tắt

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

Pre-Lambda trước 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 đòi hỏi mộtfinal int value
njzk2

8
Đúng, bạn đúng, nhưng tôi đã viết rằng vẫn sử dụng trình biên dịch Java 8 cho phép bạn sử dụng những thứ "hiệu quả cuối cùng"
Adam

1
@gstackoverflow có, nhưng sau đó java 8 không phải làpre-lambda
njzk2

1
bạn cũng có thể sử dụng kiểu pre-lambda trong java 8. Tôi đã viết nó JFY
gstackoverflow

3
Không phải lambdas đã được thực hiện chỉ để mọi người SO có thể ghi điểm? :-)
Stephane

48

An IntFunction<R>là một hàm int -> R. An IntUnaryOperatorlà một hàm int -> int.

Vì vậy, an IntFunction<IntUnaryOperator>là một hàm nhận một inttham số là và trả về một hàm nhận một inttham số dưới dạng và trả về một 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

Có thể rõ ràng hơn nếu bạn sử dụng các lớp ẩn danh để "phân hủy" 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

Thêm dấu ngoặc đơn có thể làm cho điều này rõ ràng hơn:

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

Hoặc có thể biến trung gian có thể giúp:

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

24

Hãy viết lại biểu thức lambda đó bằng dấu ngoặc đơn để làm rõ ràng hơn:

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

Vì vậy, chúng tôi đang khai báo một hàm lấy an intmà trả về a Function. Cụ thể hơn, hàm được trả về nhận một intvà trả về một int(tổng của hai phần tử): điều này có thể được biểu diễn dưới dạng một IntUnaryOperator.

Do đó, curriedAddlà một hàm nhận một intvà trả về một IntUnaryOperator, vì vậy nó có thể được biểu diễn dưới dạng IntFunction<IntUnaryOperator>.


9

Đó là hai biểu thức 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

Nếu bạn nhìn vào IntFunctionnó có thể trở nên rõ ràng hơn: IntFunction<R>là a FunctionalInterface. Nó đại diện cho một hàm nhận một intvà trả về một giá trị kiểu R.

Trong trường hợp này, kiểu trả về Rcũng là a FunctionalInterface, cụ thể là an IntUnaryOperator. Vì vậy, bản thân hàm đầu tiên (bên ngoài) trả về một hàm.

Trong trường hợp này: Khi được áp dụng cho an int, curriedAddđược cho là trả về một hàm lại nhận một int(và trả về một lần nữa int, vì đó là những gì IntUnaryOperatorlàm được).

Trong lập trình hàm, người ta thường viết kiểu của hàm là param -> return_valuevà bạn thấy chính xác ở đây. Vì vậy, loại curriedAddint -> int -> int(hoặc int -> (int -> int)nếu bạn thích điều đó tốt hơn).

Cú pháp lambda của Java 8 đi cùng với điều này. Để xác định một chức năng như vậy, bạn viết

a -> b -> a + b

rất giống với phép tính lambda thực tế:

λa λb a + b

λb a + blà một hàm nhận một tham số duy nhất bvà trả về một giá trị (tổng). λa λb a + blà một hàm chấp nhận một tham số duy nhất avà trả về một hàm khác của một tham số duy nhất. λa λb a + btrả về λb a + bvới agiá trị tham số được đặt.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.