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?
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?
Odpowiedzi:
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 0
i 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
}
reduce
nie przyjmuje wartości początkowej, ale zamiast tego zaczyna się od pierwszego elementu kolekcji jako akumulatora (wywoływanego sum
w 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 1
i 2
.
Możesz użyć, reduce
gdy Twoja operacja nie zależy od żadnych wartości innych niż te w kolekcji, do której ją stosujesz.
emptyList<Int>().reduce { acc, s -> acc + s }
stworzy wyjątek, ale emptyList<Int>().fold(0) { acc, s -> acc + s }
jest OK.
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)
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ż .reduce
nie 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 .fold
to pozwoli), jeśli Twoja początkowa kolekcja może być pusta, musisz albo sprawdzić swoją kolekcję rozmiar, a następnie .reduce
lub 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)