Lihatlah ini untuk diskusi mendasar yang mendalam tentang topik ini
GhostCat
2
@ LunarWatcher, saya melihat dokumen itu, tetapi tidak mendapatkannya, itu pertanyaan yang diposting, bisakah Anda memberi contoh?
TapanHP
1
@MattKlein selesai
Jayson Minard
Jawaban:
281
fold mengambil nilai awal, dan permintaan pertama dari lambda yang Anda lewati akan menerima nilai awal dan elemen pertama dari koleksi sebagai parameter.
Misalnya, ambil kode berikut yang menghitung jumlah daftar bilangan bulat:
listOf(1,2,3).fold(0){ sum, element -> sum + element }
Panggilan pertama ke lambda akan dengan parameter 0dan 1.
Memiliki kemampuan untuk memberikan nilai awal berguna jika Anda harus memberikan semacam nilai atau parameter default untuk operasi Anda. Misalnya, jika Anda mencari nilai maksimum di dalam daftar, tetapi karena alasan tertentu ingin mengembalikan setidaknya 10, Anda dapat melakukan hal berikut:
listOf(1,6,4).fold(10){ max, element ->if(element > max) element else max
}
reducetidak mengambil nilai awal, melainkan mulai dengan elemen pertama koleksi sebagai akumulator (disebut sumdalam contoh berikut).
Sebagai contoh, mari kita lakukan jumlah bilangan bulat lagi:
listOf(1,2,3).reduce { sum, element -> sum + element }
Panggilan pertama ke lambda di sini akan dengan parameter 1dan 2.
Anda dapat menggunakannya reducesaat operasi Anda tidak bergantung pada nilai apa pun selain dari yang ada di koleksi yang Anda lamar.
Penjelasan yang bagus! Saya akan mengatakan juga, bahwa koleksi kosong tidak dapat dikurangi, tetapi dapat dilipat.
Miha_x64
lihat, m pada level paling pemula di Kotlin, contoh pertama yang Anda berikan dapat Anda jelaskan lebih banyak dengan beberapa langkah, dan jawaban akhir? akan sangat membantu
TapanHP
3
@TapanHP emptyList<Int>().reduce { acc, s -> acc + s }akan menghasilkan pengecualian, tetapi emptyList<Int>().fold(0) { acc, s -> acc + s }tidak masalah.
Miha_x64
31
mengurangi juga memaksa kembalinya lambda menjadi tipe yang sama dengan anggota daftar, yang tidak benar dengan lipatan. Ini adalah konsekuensi penting dari pembuatan elemen pertama dari daftar, nilai awal akumulator.
andresp
4
@andresp: sama seperti catatan untuk kelengkapan: tidak harus dengan tipe yang sama . Anggota daftar juga dapat menjadi subtipe akumulator: ini berfungsi listOf<Int>(1, 2).reduce { acc: Number, i: Int -> acc.toLong() + i }(daftar-jenisnya Int sedangkan tipe akumulator dinyatakan sebagai Angka dan sebenarnya Panjang)
Boris
11
Perbedaan fungsional utama yang saya sebut (yang disebutkan dalam komentar pada jawaban lain, tetapi mungkin sulit dimengerti) adalah bahwa reduceakan mengeluarkan pengecualian jika dilakukan pada koleksi kosong.
listOf<Int>().reduce { x, y -> x + y }// java.lang.UnsupportedOperationException: Empty collection can't be reduced.
Ini karena .reducetidak tahu nilai apa yang dikembalikan jika "tidak ada data".
Bandingkan dengan ini .fold, yang mengharuskan Anda untuk memberikan "nilai awal", yang akan menjadi nilai default jika koleksi kosong:
val result = listOf<Int>().fold(0){ x, y -> x + y }
assertEquals(0, result)
Jadi, bahkan jika Anda tidak ingin mengagregasi koleksi Anda ke satu elemen dari tipe (tidak terkait) yang berbeda (yang hanya .foldakan membiarkan Anda melakukannya), jika koleksi awal Anda mungkin kosong maka Anda harus memeriksa koleksi Anda ukuran dulu dan kemudian .reduce, atau gunakan saja.fold
val collection:List<Int>=// collection of unknown sizeval result1=if(collection.isEmpty())0else collection.reduce { x, y -> x + y }val result2= collection.fold(0){ x, y -> x + y }
assertEquals(result1, result2)
Jawaban:
fold
mengambil nilai awal, dan permintaan pertama dari lambda yang Anda lewati akan menerima nilai awal dan elemen pertama dari koleksi sebagai parameter.Misalnya, ambil kode berikut yang menghitung jumlah daftar bilangan bulat:
Panggilan pertama ke lambda akan dengan parameter
0
dan1
.Memiliki kemampuan untuk memberikan nilai awal berguna jika Anda harus memberikan semacam nilai atau parameter default untuk operasi Anda. Misalnya, jika Anda mencari nilai maksimum di dalam daftar, tetapi karena alasan tertentu ingin mengembalikan setidaknya 10, Anda dapat melakukan hal berikut:
reduce
tidak mengambil nilai awal, melainkan mulai dengan elemen pertama koleksi sebagai akumulator (disebutsum
dalam contoh berikut).Sebagai contoh, mari kita lakukan jumlah bilangan bulat lagi:
Panggilan pertama ke lambda di sini akan dengan parameter
1
dan2
.Anda dapat menggunakannya
reduce
saat operasi Anda tidak bergantung pada nilai apa pun selain dari yang ada di koleksi yang Anda lamar.sumber
emptyList<Int>().reduce { acc, s -> acc + s }
akan menghasilkan pengecualian, tetapiemptyList<Int>().fold(0) { acc, s -> acc + s }
tidak masalah.listOf<Int>(1, 2).reduce { acc: Number, i: Int -> acc.toLong() + i }
(daftar-jenisnya Int sedangkan tipe akumulator dinyatakan sebagai Angka dan sebenarnya Panjang)Perbedaan fungsional utama yang saya sebut (yang disebutkan dalam komentar pada jawaban lain, tetapi mungkin sulit dimengerti) adalah bahwa
reduce
akan mengeluarkan pengecualian jika dilakukan pada koleksi kosong.Ini karena
.reduce
tidak tahu nilai apa yang dikembalikan jika "tidak ada data".Bandingkan dengan ini
.fold
, yang mengharuskan Anda untuk memberikan "nilai awal", yang akan menjadi nilai default jika koleksi kosong:Jadi, bahkan jika Anda tidak ingin mengagregasi koleksi Anda ke satu elemen dari tipe (tidak terkait) yang berbeda (yang hanya
.fold
akan membiarkan Anda melakukannya), jika koleksi awal Anda mungkin kosong maka Anda harus memeriksa koleksi Anda ukuran dulu dan kemudian.reduce
, atau gunakan saja.fold
sumber