Kotlin'de katlama ve azaltma arasındaki temel fark nedir? Hangisi ne zaman kullanılır?


132

Bu iki işlevle oldukça kafam karıştı fold()ve reduce()Kotlin'de, biri bana her ikisini de ayıran somut bir örnek verebilir mi?



4
Göz at bu bu konunun derin bir temel tartışma için
GhostCat

2
@LunarWatcher, o dokümanları gördüm, ama anlamadım, bu y postalandı, örnek verebilir misin?
TapanHP

1
@MattKlein bitti
Jayson Minard

Yanıtlar:


281

fold bir başlangıç ​​değeri alır ve ona ilettiğiniz lambda'nın ilk çağrısı, bu başlangıç ​​değerini ve koleksiyonun ilk öğesini parametre olarak alır.

Örneğin, bir tamsayı listesinin toplamını hesaplayan aşağıdaki kodu alın:

listOf(1, 2, 3).fold(0) { sum, element -> sum + element }

Lambda ilk çağrı parametreleri ile olacak 0ve 1.

İşleminiz için bir tür varsayılan değer veya parametre sağlamanız gerekiyorsa, bir başlangıç ​​değerini iletme yeteneğine sahip olmak yararlıdır. Örneğin, bir listedeki maksimum değeri aradıysanız, ancak herhangi bir nedenle en az 10 döndürmek istiyorsanız, aşağıdakileri yapabilirsiniz:

listOf(1, 6, 4).fold(10) { max, element ->
    if (element > max) element else max
}

reducebir başlangıç ​​değeri almaz, bunun yerine biriktirici olarak koleksiyonun ilk öğesi ile başlar ( sumaşağıdaki örnekte çağrılır ).

Örneğin, tekrar bir tamsayı toplamı yapalım:

listOf(1, 2, 3).reduce { sum, element -> sum + element }

Burada lambda ilk çağrı parametreleri ile olacak 1ve 2.

Sen kullanabilirsiniz reducesizin işlem, bunu uyguluyorsanız koleksiyonunda dışında herhangi değerlere bağlı değildir zaman.


47
İyi açıklama! Ayrıca şunu da söyleyebilirim ki, bu boş koleksiyon küçültülemez ama katlanabilir.
Miha_x64

Bakın, Kotlin'de çok başlangıç ​​seviyesindeyim, verdiğiniz ilk örnek birkaç adım ve son cevabıyla daha fazla açıklayabilir misiniz? çok yardımcı olur
TapanHP

3
@ TapanHP emptyList<Int>().reduce { acc, s -> acc + s }bir istisna oluşturacak, ancak emptyList<Int>().fold(0) { acc, s -> acc + s }sorun değil.
Miha_x64

31
azaltma ayrıca lambda'nın dönüşünü liste üyeleriyle aynı türde olmaya zorlar, bu da katlama için doğru değildir. Bu, listenin ilk öğesini, akümülatörün başlangıç ​​değerini yapmanın önemli bir sonucudur.
andresp

4
@andresp: tamlık için bir not olarak: aynı türde olması gerekmez . Liste üyeleri aynı zamanda toplayıcının bir alt türü de olabilir: bu işe yarar listOf<Int>(1, 2).reduce { acc: Number, i: Int -> acc.toLong() + i }(liste türü Int iken toplayıcı türü Sayı olarak bildirilir ve aslında bir
Boris

11

Söyleyeceğim en büyük işlevsel fark (diğer cevaptaki yorumlarda belirtilmiştir, ancak anlaşılması zor olabilir), boş bir koleksiyon üzerinde gerçekleştirilirse reduce bir istisna atacak olmasıdır .

listOf<Int>().reduce { x, y -> x + y }
// java.lang.UnsupportedOperationException: Empty collection can't be reduced.

Bunun nedeni .reduce, "veri olmaması" durumunda hangi değerin döndürüleceğini bilmemesidir.

Bunu .fold, boş bir koleksiyon olması durumunda varsayılan değer olacak bir "başlangıç ​​değeri" sağlamanızı gerektiren şununla karşılaştırın :

val result = listOf<Int>().fold(0) { x, y -> x + y }
assertEquals(0, result)

Bu nedenle, koleksiyonunuzu farklı (ilgisiz) türden tek bir öğeye indirgemek istemeseniz bile (bu yalnızca .foldsize izin verir), başlangıç ​​koleksiyonunuz boşsa, o zaman koleksiyonunuzu kontrol etmelisiniz. önce boyut ve sonra .reduceveya sadece kullanın.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)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.