Jaka jest podstawowa różnica między fold i redukuj w Kotlinie? Kiedy używać którego?


130

Jestem dość zdezorientowany z tymi obiema funkcjami fold()iw reduce()Kotlinie, czy ktoś może mi podać konkretny przykład, który odróżnia je obie?



4
Spójrz na to, aby uzyskać głęboką, fundamentalną dyskusję na ten temat
GhostCat

2
@LunarWatcher, widziałem te dokumenty, ale ich nie rozumiem, to jest twoje pytanie, czy możesz podać przykład?
TapanHP,

1
@MattKlein done
Jayson Minard

Odpowiedzi:


280

fold przyjmuje wartość początkową, a pierwsze wywołanie wyrażenia lambda, które do niego przekazujesz, otrzyma tę wartość początkową i pierwszy element kolekcji jako parametry.

Na przykład weźmy następujący kod, który oblicza sumę listy liczb całkowitych:

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

Pierwsze wywołanie lambdy będzie miało parametry 0i 1.

Możliwość przekazania wartości początkowej jest przydatna, jeśli musisz podać jakąś domyślną wartość lub parametr dla swojej operacji. Na przykład, jeśli szukałeś maksymalnej wartości na liście, ale z jakiegoś powodu chcesz zwrócić co najmniej 10, możesz wykonać następujące czynności:

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

reducenie przyjmuje wartości początkowej, ale zamiast tego zaczyna się od pierwszego elementu kolekcji jako akumulatora (wywoływanego sumw poniższym przykładzie).

Na przykład zróbmy ponownie sumę liczb całkowitych:

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

Pierwsze wywołanie lambdy tutaj będzie miało parametry 1i 2.

Możesz użyć, reducegdy Twoja operacja nie zależy od żadnych wartości innych niż te w kolekcji, do której ją stosujesz.


47
Dobre wytłumaczenie! Powiedziałbym również, że pustej kolekcji nie można zmniejszyć, ale można ją złożyć.
Miha_x64

widzisz, m na bardzo początkującym poziomie w Kotlinie, pierwszy przykład, który podałeś, czy możesz wyjaśnić go dokładniej, wykonując kilka kroków i ostateczną odpowiedź? byłaby bardzo pomocna
TapanHP

3
@TapanHP emptyList<Int>().reduce { acc, s -> acc + s }stworzy wyjątek, ale emptyList<Int>().fold(0) { acc, s -> acc + s }jest OK.
Miha_x64

31
Redukcja wymusza również, aby zwracana lambda była tego samego typu, co elementy listy, co nie jest prawdą w przypadku fold. To ważna konsekwencja uczynienia pierwszego elementu listy, początkowej wartości akumulatora.
andresp

4
@andresp: tylko jako uwaga dotycząca kompletności: nie musi być tego samego typu. Członkowie listy mogą być również podtypem akumulatora: to działa listOf<Int>(1, 2).reduce { acc: Number, i: Int -> acc.toLong() + i }(typem listy jest Int, podczas gdy typ akumulatora jest zadeklarowany jako Liczba, a właściwie jest to Long)
Boris

11

Główną różnicą funkcjonalną, którą chciałbym wskazać (o której wspomina się w komentarzach do drugiej odpowiedzi, ale może być trudna do zrozumienia), jest to, że reduce zgłosi wyjątek, jeśli zostanie wykonany na pustej kolekcji.

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

Dzieje się tak, ponieważ .reducenie wie, jaką wartość zwrócić w przypadku braku danych.

W przeciwieństwie do tego .fold, który wymaga podania „wartości początkowej”, która będzie wartością domyślną w przypadku pustej kolekcji:

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

Tak więc, nawet jeśli nie chcesz agregować swojej kolekcji do pojedynczego elementu innego (niezwiązanego) typu (co tylko na .foldto pozwoli), jeśli Twoja początkowa kolekcja może być pusta, musisz albo sprawdzić swoją kolekcję rozmiar, a następnie .reducelub po prostu użyj.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)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.