Was ist der grundlegende Unterschied zwischen Falten und Reduzieren in Kotlin? Wann welche verwenden?

130

Ich bin ziemlich verwirrt mit diesen beiden Funktionen fold()und reduce()kann mir in Kotlin jemand ein konkretes Beispiel geben, das beide unterscheidet?

TapanHP
quelle
2
falten und reduzieren .
Zoe
4
Schauen Sie sich dies für eine tiefe grundlegende Diskussion dieses Themas an
GhostCat
2
@LunarWatcher, ich habe diese Dokumente gesehen, aber nicht verstanden, das ist eine Frage, können Sie ein Beispiel geben?
TapanHP
1
@ MattKlein fertig
Jayson Minard

Antworten:

280

fold Nimmt einen Anfangswert und der erste Aufruf des Lambdas, das Sie an ihn übergeben, erhält diesen Anfangswert und das erste Element der Sammlung als Parameter.

Nehmen Sie zum Beispiel den folgenden Code, der die Summe einer Liste von ganzen Zahlen berechnet:

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

Der erste Aufruf des Lambda erfolgt mit den Parametern 0und 1.

Die Möglichkeit, einen Anfangswert zu übergeben, ist nützlich, wenn Sie einen Standardwert oder -parameter für Ihre Operation angeben müssen. Wenn Sie beispielsweise nach dem Maximalwert in einer Liste suchen, aber aus irgendeinem Grund mindestens 10 zurückgeben möchten, können Sie Folgendes tun:

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

reducenimmt keinen Anfangswert an, sondern beginnt mit dem ersten Element der Sammlung als Akkumulator ( sumim folgenden Beispiel aufgerufen ).

Lassen Sie uns zum Beispiel noch einmal eine Summe von ganzen Zahlen machen:

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

Der erste Aufruf des Lambda erfolgt hier mit den Parametern 1und 2.

Sie können verwenden, reducewenn Ihre Operation nicht von anderen Werten als denen in der Sammlung abhängt, auf die Sie sie anwenden.

zsmb13
quelle
47
Gute Erklärung! Ich würde auch sagen, dass die leere Sammlung nicht reduziert, sondern gefaltet werden kann.
Miha_x64
Sehen Sie, ich bin ein Anfänger in Kotlin. Das allererste Beispiel, das Sie gegeben haben, können Sie mit einigen Schritten und der endgültigen Antwort näher erläutern. wäre eine große Hilfe
TapanHP
3
@TapanHP emptyList<Int>().reduce { acc, s -> acc + s }erzeugt eine Ausnahme, ist aber emptyList<Int>().fold(0) { acc, s -> acc + s }in Ordnung.
Miha_x64
31
Reduzieren erzwingt auch, dass die Rückgabe des Lambda vom gleichen Typ wie die Listenmitglieder ist, was bei Fold nicht der Fall ist. Dies ist eine wichtige Folge davon, dass das erste Element der Liste der Anfangswert des Akkumulators ist.
andresp
4
@andresp: Nur als Hinweis zur Vollständigkeit: Es muss nicht der gleiche Typ sein. Die Listenmitglieder können auch ein Subtyp des Akkumulators sein: Dies funktioniert listOf<Int>(1, 2).reduce { acc: Number, i: Int -> acc.toLong() + i }(der Listentyp ist Int, während der Akkumulatortyp als Number deklariert ist und tatsächlich ein Long ist)
Boris
11

Der Hauptfunktionsunterschied, den ich hervorheben würde (der in den Kommentaren zur anderen Antwort erwähnt wird, aber möglicherweise schwer zu verstehen ist), besteht darin, dass reduce eine Ausnahme ausgelöst wird, wenn er für eine leere Sammlung ausgeführt wird.

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

Dies liegt daran, .reducedass nicht bekannt ist, welcher Wert bei "keine Daten" zurückgegeben werden soll.

Vergleichen Sie dies damit .fold, dass Sie einen "Startwert" angeben müssen, der im Falle einer leeren Sammlung der Standardwert ist:

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

Selbst wenn Sie Ihre Sammlung nicht zu einem einzigen Element eines anderen (nicht verwandten) Typs zusammenfassen möchten (was nur .foldmöglich ist), müssen Sie entweder Ihre Sammlung überprüfen, wenn Ihre Startsammlung möglicherweise leer ist Größe zuerst und dann .reduceoder einfach verwenden.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)
Matt Klein
quelle