แลมบ์ดาที่มีลูกศร 2 ตัวใน Java 8 หมายถึงอะไร


118

ฉันเคยอ่านบทแนะนำ Java 8 มาแล้วหลายครั้ง

ตอนนี้ฉันพบหัวข้อต่อไปนี้: java รองรับ Currying หรือไม่?

ที่นี่ฉันเห็นรหัสต่อไปนี้:

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

ฉันเข้าใจว่าตัวอย่างนี้รวม 2 องค์ประกอบ แต่ฉันไม่เข้าใจโครงสร้าง:

a -> b -> a + b;

ตามส่วนซ้ายของนิพจน์แถวนี้ควรใช้ฟังก์ชันต่อไปนี้:

R apply(int value); 

ก่อนหน้านี้ฉันได้พบกับลูกศรเพียงลูกเดียวเท่านั้น


6
คุณเข้าใจความหมายของแกงกะหรี่หรือไม่? นั่นเป็นส่วนสำคัญของคำถามนี้
BlueRaja - Danny Pflughoeft

12
มันเป็นเพียงแลมด้าที่คืนค่าแลมด้า
Ilmari Karonen

คำตอบ:


118

ถ้าคุณแสดงสิ่งนี้เป็นไวยากรณ์แลมบ์ดาที่ไม่ใช่ชวเลขหรือไวยากรณ์คลาสก่อนแลมด้า Java แบบไม่ระบุชื่อมันจะชัดเจนกว่าว่าเกิดอะไรขึ้น ...

คำถามเดิม ทำไมลูกศรสองดอก? อย่างง่ายมีการกำหนดฟังก์ชันสองฟังก์ชัน ... ฟังก์ชันแรกคือฟังก์ชันกำหนดฟังก์ชันส่วนที่สองเป็นผลลัพธ์ของฟังก์ชันนั้นซึ่งเป็นฟังก์ชันด้วย แต่ละรายการต้องใช้ไฟล์->ดำเนินการเพื่อกำหนด

Non-ชวเลข

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

Pre-Lambda ก่อน 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 ต้องการ afinal int value
njzk2

8
ใช่คุณถูกต้อง แต่ฉันเขียนว่ายังคงใช้คอมไพเลอร์ Java 8 ซึ่งช่วยให้คุณสามารถใช้สิ่งที่ "ขั้นสุดท้ายได้อย่างมีประสิทธิภาพ"
อดัม

1
@gstackoverflow ใช่ แต่ java 8 ไม่ใช่pre-lambda
njzk2

1
คุณสามารถใช้ pre-lambda style ใน java 8 ได้เช่นกัน ฉันเขียนมัน JFY
gstackoverflow

3
แลมบดัสไม่ได้ทำเพียงเพื่อให้คนอื่น ๆ ทำคะแนนได้? :-)
Stephane

48

เป็นฟังก์ชั่นIntFunction<R> เป็นฟังก์ชั่นint -> RIntUnaryOperatorint -> int

ดังนั้นจึงIntFunction<IntUnaryOperator>เป็นฟังก์ชันที่ใช้ไฟล์intพารามิเตอร์ as และส่งคืนฟังก์ชันที่ใช้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ที่คืนค่า a Function. โดยเฉพาะอย่างยิ่งฟังก์ชันที่ส่งคืนรับintและส่งกลับint(ผลรวมของสององค์ประกอบ): สิ่งนี้สามารถแสดงเป็นไฟล์IntUnaryOperatorไฟล์.

ดังนั้นจึงcurriedAddเป็นฟังก์ชันที่รับintและส่งคืนค่าIntUnaryOperatorดังนั้นจึงสามารถแสดงเป็นIntFunction<IntUnaryOperator>.



8

หากดูIntFunctionแล้วอาจจะชัดเจนขึ้น: IntFunction<R>คือFunctionalInterface. แสดงถึงฟังก์ชันที่ใช้intและส่งกลับค่าของชนิดRและส่งกลับค่าของชนิด

ในกรณีนี้ประเภทการส่งคืนRยังเป็น a FunctionalInterfaceกล่าวคือIntUnaryOperator. อย่างแรกฟังก์ชัน (ภายนอก) จะส่งคืนฟังก์ชัน

ในกรณีนี้: เมื่อนำไปใช้กับint, curriedAddควรจะกลับมาอีกครั้งที่ฟังก์ชั่นใช้เวลาอีกด้วยint(และผลตอบแทนอีกครั้งintเนื่องจากว่าสิ่งที่IntUnaryOperatorไม่)

ในการเขียนโปรแกรมเชิงฟังก์ชันเป็นเรื่องปกติที่จะเขียนประเภทของฟังก์ชันเป็นparam -> return_valueและคุณจะเห็นว่าที่นี่ ประเภทของcurriedAddคือint -> 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 + bพร้อมaตั้งค่าเป็นค่าพารามิเตอร์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.