ฉันค่อนข้างสับสนกับทั้งสองฟังก์ชั่นนี้fold()
และreduce()
ใน Kotlin ใครช่วยยกตัวอย่างที่เป็นรูปธรรมที่ทำให้ทั้งสองอย่างเห็นได้ชัด
ฉันค่อนข้างสับสนกับทั้งสองฟังก์ชั่นนี้fold()
และreduce()
ใน Kotlin ใครช่วยยกตัวอย่างที่เป็นรูปธรรมที่ทำให้ทั้งสองอย่างเห็นได้ชัด
คำตอบ:
fold
รับค่าเริ่มต้นและการเรียกใช้ lambda ครั้งแรกที่คุณส่งผ่านไปจะได้รับค่าเริ่มต้นนั้นและองค์ประกอบแรกของคอลเล็กชันเป็นพารามิเตอร์
ตัวอย่างเช่นใช้รหัสต่อไปนี้ที่คำนวณผลรวมของรายการจำนวนเต็ม:
listOf(1, 2, 3).fold(0) { sum, element -> sum + element }
การเรียกแลมบ์ดาครั้งแรกจะมีพารามิเตอร์0
และ1
.
การมีความสามารถในการส่งผ่านค่าเริ่มต้นจะมีประโยชน์หากคุณต้องระบุค่าเริ่มต้นหรือพารามิเตอร์สำหรับการดำเนินการของคุณ ตัวอย่างเช่นหากคุณกำลังมองหาค่าสูงสุดในรายการ แต่ด้วยเหตุผลบางประการที่ต้องการส่งคืนอย่างน้อย 10 คุณสามารถดำเนินการดังต่อไปนี้:
listOf(1, 6, 4).fold(10) { max, element ->
if (element > max) element else max
}
reduce
ไม่ใช้ค่าเริ่มต้น แต่เริ่มต้นด้วยองค์ประกอบแรกของคอลเล็กชันเป็นตัวสะสม (เรียกsum
ในตัวอย่างต่อไปนี้)
ตัวอย่างเช่นลองหาผลรวมของจำนวนเต็มอีกครั้ง:
listOf(1, 2, 3).reduce { sum, element -> sum + element }
การเรียกแลมบ์ดาครั้งแรกที่นี่จะเป็นพารามิเตอร์1
และ2
.
คุณสามารถใช้ได้reduce
เมื่อการดำเนินการของคุณไม่ขึ้นอยู่กับค่าอื่นใดนอกจากค่าในคอลเล็กชันที่คุณใช้
emptyList<Int>().reduce { acc, s -> acc + s }
จะสร้างข้อยกเว้น แต่emptyList<Int>().fold(0) { acc, s -> acc + s }
ก็โอเค
listOf<Int>(1, 2).reduce { acc: Number, i: Int -> acc.toLong() + i }
(ประเภทรายการคือ Int ในขณะที่ประเภทตัวสะสมถูกประกาศเป็น Number และเป็นแบบยาว)
ความแตกต่างของฟังก์ชันที่สำคัญที่ฉันจะเรียก (ซึ่งกล่าวถึงในความคิดเห็นเกี่ยวกับคำตอบอื่น ๆ แต่อาจยากที่จะเข้าใจ) คือreduce
จะทำให้เกิดข้อยกเว้นหากดำเนินการกับคอลเล็กชันที่ว่างเปล่า
listOf<Int>().reduce { x, y -> x + y }
// java.lang.UnsupportedOperationException: Empty collection can't be reduced.
เนื่องจาก.reduce
ไม่ทราบว่าจะส่งคืนค่าใดในกรณีที่ "ไม่มีข้อมูล"
ตรงกันข้ามกับสิ่งนี้.fold
ซึ่งคุณต้องระบุ "ค่าเริ่มต้น" ซึ่งจะเป็นค่าเริ่มต้นในกรณีที่คอลเลกชันว่างเปล่า:
val result = listOf<Int>().fold(0) { x, y -> x + y }
assertEquals(0, result)
ดังนั้นแม้ว่าคุณจะไม่ต้องการรวมคอลเลกชันของคุณเป็นองค์ประกอบเดียวของประเภทอื่น (ที่ไม่เกี่ยวข้อง) (ซึ่ง.fold
จะให้คุณทำเท่านั้น) หากคอลเล็กชันเริ่มต้นของคุณอาจว่างเปล่าคุณต้องตรวจสอบคอลเล็กชันของคุณ ขนาดก่อนแล้ว.reduce
หรือเพียงแค่ใช้.fold
val collection: List<Int> = // collection of unknown size
val result1 = if (collection.isEmpty()) 0
else collection.reduce { x, y -> x + y }
val result2 = collection.fold(0) { x, y -> x + y }
assertEquals(result1, result2)
ลด - มีreduce()
วิธีการเปลี่ยนให้คอลเลกชันเป็นผลเดียว
val numbers: List<Int> = listOf(1, 2, 3)
val sum: Int = numbers.reduce { acc, next -> acc + next }
//sum is 6 now.
พับ - จะเกิดอะไรขึ้นในกรณีก่อนหน้านี้ของรายการว่าง ? ที่จริงแล้วไม่มีค่าที่ถูกต้องที่จะส่งคืนดังนั้นจึงreduce()
โยน aRuntimeException
ในกรณีfold
นี้เป็นเครื่องมือที่มีประโยชน์ คุณสามารถใส่ค่าเริ่มต้นได้ -
val sum: Int = numbers.fold(0, { acc, next -> acc + next })
ที่นี่เราได้ระบุค่าเริ่มต้น ในทางตรงกันข้ามไปreduce()
ถ้าคอลเลกชันนี้เป็นที่ว่างเปล่า , RuntimeException
ค่าเริ่มต้นจะถูกส่งกลับซึ่งจะป้องกันไม่ให้คุณจาก