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.
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 reducezgł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 sizeval result1=if(collection.isEmpty())0else collection.reduce { x, y -> x + y }val result2= collection.fold(0){ x, y -> x + y }
assertEquals(result1, result2)
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:
Pierwsze wywołanie lambdy będzie miało parametry
0
i1
.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:
reduce
nie przyjmuje wartości początkowej, ale zamiast tego zaczyna się od pierwszego elementu kolekcji jako akumulatora (wywoływanegosum
w poniższym przykładzie).Na przykład zróbmy ponownie sumę liczb całkowitych:
Pierwsze wywołanie lambdy tutaj będzie miało parametry
1
i2
.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.źródło
emptyList<Int>().reduce { acc, s -> acc + s }
stworzy wyjątek, aleemptyList<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.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: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
źródło