Mechanizm reduce odnosi się do operacji funkcjonalnych na kolekcjach danych i przybył do Swift z języków funkcyjnych (map-reduce, fold). Historycznie ta funkcja pozwala na przekształcenie dowolnej kolekcji w jedną zagregowaną wartość (np. suma, iloczyn, połączenie łańcuchów itp.), przechodząc przez wszystkie jej elementy i kumulując wynik. Podstawowy problem, który rozwiązuje — zwięzłe, czytelne i wolne od błędów agregowanie danych zamiast ręcznych pętli.
W Swift reduce jest zdefiniowana jako metoda kolekcji:
func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result
To oznacza, że podajesz początkową wartość, a następnie piszesz funkcję, która dla każdego elementu i bieżącego akumulatora zwraca nową zagregowaną wartość.
Przykład kodu:
let numbers = [1, 2, 3, 4] let sum = numbers.reduce(0) { $0 + $1 } // 10 let joined = numbers.reduce("") { $0 + String($1) } // "1234"
Kluczowe cechy:
Jak działa reduce na pustej kolekcji?
Reduce stosuje się do każdego elementu kolekcji. Jeśli kolekcja jest pusta — zwracana jest początkowa wartość, żadne wywołania closure nie będą miały miejsca.
Przykład kodu:
let empty: [Int] = [] let sum = empty.reduce(100) { $0 + $1 } // 100
Czy można za pomocą reduce zmienić oryginalną kolekcję?
Nie, reduce to funkcja czysta, nie zmienia oryginalnej kolekcji. Wszystkie zmiany zachodzą tylko w akumulatorze.
Jaka jest różnica między reduce a reduce(into:)?
reduce(into:) pozwala na mutację akumulatora przy każdym przejściu i działa efektywniej z typami wartości, gdzie utworzenie nowej kopii (copy-on-write) jest kosztowne.
Przykład kodu:
let nums = [1, 2, 3] let squares = nums.reduce(into: []) { (result: inout [Int], item) in result.append(item * item) } // squares == [1, 4, 9]
Programista chciał zsumować ceny produktów przechowywanych w tablicy products. Użył reduce z początkową wartością 0.0, ale closure było tak zaprojektowane, że czasami zwracało ujemną sumę w przypadku rabatów, co doprowadziło do niepoprawnych wyników i problemów z obliczeniem podatku.
Zalety:
Wady:
Użyto reduce(into:) do stworzenia cache'u z tablicy encji według id:
let objects: [Entity] = ... let cache = objects.reduce(into: [:]) { $0[$1.id] = $1 }
Zalety:
Wady: