napisany przez Reinder de Vries dnia lipca 9 2020 w rozwój aplikacji, Swift
w języku Swift używasz map()
, reduce()
i filter()
, aby zapętlić zbiory, takie jak tablice i słowniki, bez użycia pętli for.
funkcje map, reduce i filter pochodzą z dziedziny programowania funkcyjnego (FP). Nazywa się je funkcjami wyższego rzędu, ponieważ przyjmują funkcje jako dane wejściowe. Na przykład stosuje się funkcję do tablicy, aby przekształcić jej dane.
funkcje mapowania, zmniejszania i filtrowania Swifta mogą być trudne do ogarnięcia. Zwłaszcza jeśli zawsze kodowałeś pętle for in
, aby rozwiązać problemy z iteracją. W tym przewodniku dowiesz się, jak korzystać z funkcji map(_:)
, reduce(_:_:)
i filter(_:)
w języku Swift.
zaczynajmy!
- Wprowadzenie do mapowania, zmniejszania i filtrowania
- Szybki Start: Funkcje wyższego rzędu w Swift
- Korzystanie z funkcji Map
- Korzystanie z funkcji Redukuj
- Korzystanie z funkcji Filtruj
- łączenie Map, Redukuj i filtruj
- Czytaj dalej
Wprowadzenie do Map, Redukuj i filtruj
podczas budowania iOS aplikacji, zazwyczaj używa się programowania proceduralnego lub Obiektowego. Programowanie funkcyjne jest inne: zajmuje się tylko funkcjami. Bez zmiennych, bez stanu, bez pętli for-tylko funkcje.
język programowania Swift doskonale nadaje się do mieszania programowania funkcyjnego z niefunkcjonalnymi podejściami, takimi jak OOP. Nie musisz ściśle pisać kodu funkcjonalnego, a przyjęcie koncepcji z programowania funkcyjnego może pomóc Ci nauczyć się lepiej kodować.
funkcje map(_:)
, reduce(_:_:)
i filter(_:)
nazywane są funkcjami wyższego rzędu, ponieważ przyjmują funkcję jako wejście i zwracają funkcję jako wyjście. Technicznie Swift zwraca wyniki operacji (tj. przekształcona tablica) przy użyciu funkcji wyższego rzędu, podczas gdy czysty język funkcyjny zwróci zbiór funkcji. W języku Swift wejściami dla tych funkcji są zamknięcia.
oto jak działają:
- funkcja
map()
stosuje funkcję do każdego elementu w kolekcji. Pomyśl o „mapowaniu” lub przekształcaniu jednego zestawu wartości w inny zestaw wartości. - funkcja
reduce()
zamienia zbiór w jedną wartość. Pomyśl o tym jak o połączeniu wielu wartości w jedną, jak o uśrednianiu zbioru liczb. - funkcja
filter()
po prostu zwraca wartości, które przeszły instrukcjęif
-i tylko wtedy, gdy ten warunek skutkowałtrue
.
w przypadku, gdy myślisz: „Słuchaj, nie potrzebuję programowania funkcyjnego ani przetwarzania danych, ponieważ moje aplikacje tego nie robią!”więc nie zatrzymuj się tutaj. W ostatnich projektach aplikacji wielokrotnie korzystałem z Map, Reduce i Filter:
- filtrowanie wartości kosztów / przychodów za pomocą
filter(_:)
, aby osiągnąć próg, przed pokazaniem wartości na wykresie liniowym - uśrednianie tysięcy ocen filmów w jedną wartość za pomocą
reduce(_:_:)
- mapowanie kilku operacji na łańcuchu z hashtagami, przekształcanie go w znormalizowaną kolekcję, z
map(_:)
mogłeś rozwiązać wszystkie te problemy za pomocą pętli for, ale zobaczysz, że użycie map()
, reduce()
i filter()
skutkuje bardziej zwięzłym, czytelnym i wydajnym kodem.
Szybki Start: Funkcje wyższego rzędu w Swift
w tym samouczku skupimy się na map()
, reduce()
i filter()
. Zanim przejdziemy dalej, oto krótki przegląd najczęstszych funkcji wyższego rzędu w języku Swift:
-
map(_:)
pętle nad każdym elementem w sekwencji, stosuje funkcję do każdego elementu i zwraca przekształcony wynik -
reduce(_:_:)
pętle nad każdym elementem w sekwencji, łączy je w jedną wartość i zwraca połączony wynik -
filter(_:)
pętle nad każdym elementem w sekwencji i zwraca wynikową sekwencję, która zawiera tylko elementy spełniające daną funkcję filtrowania -
flatMap(_:)
robi to samo comap(_:)
, tyle, że spłaszcza powstałą sekwencję, tzn. zagnieżdżone tablice są nie zagnieżdżone lub „spłaszczone” -
compactMap(_:)
robi to samo comap(_:)
, z wyjątkiem tego, że usuwanil
wartości z wynikowej sekwencji przed zwróceniem jej
możesz używać tych funkcji na tablicach, słownikach, zestawach, zakresach, sekwencjach i innych typach Swift, nad którymi możesz iterować. Jeśli chcesz dowiedzieć się więcej o compactMap(_:)
i flatMap(_:)
, sprawdź ten samouczek.
kilka typów w języku Swift, takich jak tablica i Słownik, posiada funkcje, które akceptują zamknięcia jako wejście. Szybki wybór:
-
contains(where:)
pętle nad kolekcją, stosuje predykat (zamknięcie) do każdego elementu, zwracatrue
, jeśli element spełnia predykat, w przeciwnym raziefalse
-
first(where:)
pętle nad kolekcją, stosuje predykat (zamknięcie) do każdego elementu i zwraca element, jeśli spełnia predykat -
firstIndex(where:)
robi to samo cofirst(where:)
, z wyjątkiem tego, że zwraca indeks zamiast wartości
możesz dowiedzieć się więcej o tych funkcjach w tym samouczku. Jak where
jest używany w języku Swift jest również interesujące, możesz dowiedzieć się więcej na ten temat w tym samouczku.
w tym samouczku widziałeś funkcje „map” i „reduce” w języku Swift napisane jako map(_:)
i reduce(_:_:)
. Podkreślniki i dwukropki w tych funkcjach są częścią podpisu funkcji, który jest specjalnym formatem wskazującym parametry funkcji. Na przykład funkcja map(_:)
ma jeden nienazwany parametr, podczas gdy funkcja reduce(_:_:)
ma dwa. Możesz dowiedzieć się więcej na ten temat w tym samouczku: funkcje w języku Swift wyjaśnione.

Zostań programistą iOS
Naucz się tworzyć aplikacje na iOS 14 za pomocą Swift 5
Zapisz się na mój kurs rozwoju iOS i dowiedz się, jak rozpocząć karierę jako profesjonalny programista iOS.
Korzystanie z funkcji Map
funkcja map(_:)
zapętla każdy element w kolekcji i stosuje operację do każdego elementu w kolekcji. Zwraca kolekcję wynikowych elementów, do których została zastosowana operacja.
spójrzmy na przykład. Mamy szereg temperatur w stopniach Celsjusza, które chcesz przekształcić w Fahrenheita.
przydałaby się pętla for, jak ta:
let celsius = var fahrenheit: = for value in celsius { fahrenheit += }print(fahrenheit)// Output:
kod działa dobrze, ale jest zbyt gadatliwy. Potrzebujesz zmiennej” helper ” fahrenheit
, aby przechowywać obliczone konwersje podczas ich pracy, i potrzebujesz 3 linii kodu do samej konwersji.
oto jak możemy zrobić to samo z funkcją map(_:)
:
let fahrenheit = celsius.mapka { $0 * (9/5) + 32 }
druk (fahrenheit)
możesz nawet zrobić to wszystko w jednej linii:
.map { * (9/5) + 32 }
co tu się dzieje?
- zdefiniowana jest stała
celsius
, tablica podwójnych wartości i zainicjalizowana kilkoma losowymi wartościami Celsjusza. - funkcja
map(_:)
jest wywoływana w tablicycelsius
. Funkcja ma jeden argument, zamknięcie, które konwertuje z Celsjusza na Fahrenheita. - na koniec wypisywany jest wynik: przekonwertowana tablica z Celsjusza na Fahrenheita.
funkcja map(_:)
przekształca jedną tablicę w drugą, stosując funkcję do każdego elementu tablicy. Zamknięcie * (9/5) + 32
pobiera wartość wejściową w stopniach Celsjusza i zwraca wartość w stopniach Fahrenheita. Wynikowa tablica map(_:)
jest zbudowana z tych przekonwertowanych wartości.
przyjrzyjmy się bliżej zamknięciu. Jeśli wcześniej pracowałeś z zamknięciami, rozpoznasz składnię zamykania z Krótkimi Rękami. Jest to krótszy sposób kodowania zamknięcia, pomijając większość jego składni.
oto mniej zwięzła alternatywa:
let celsius = let fahrenheit = celsius.map({ (value: Double) -> Double in return value * (9/5) + 32})print(fahrenheit)
rzeczywiste wywołanie funkcji map(_:)
i jej zamknięcie jest takie:
··· = celsius.map({ (value: Double) -> Double in return value * (9/5) + 32})
co się tam dzieje? Funkcja map(_:)
jest wywoływana na tablicy celsius
. Przyjmuje jeden argument: Zamknięcie typu (Double) -> Double
.
pierwsza część zamknięcia, rozpoczynająca się od {
, wskazuje, że to zamknięcie ma jeden parametr value
typu Double
, a zamknięcie Zwraca wartość typu Double
. Ciało zamknięcia, zaczynając od return
, po prostu zwraca wynik obliczeń w stopniach Celsjusza do Fahrenheita.
jeśli porównasz składnię zamknięcia krótkiego do kodu rozszerzonego powyżej, zobaczysz, że:
- nawiasy funkcji
(
i)
są pomijane, ponieważ można je pominąć, gdy ostatni parametr wywołania funkcji jest zamknięciem. - część
() -> in
może zostać pominięta, ponieważ Swift może wywnioskować, że używasz jednego parametruDouble
jako wejścia i oczekuje się, że zwróciDouble
. Teraz, gdy pominąłeś zmiennąvalue
, możesz użyć krótkiej ręki.
- polecenie
return
również można pominąć, ponieważ oczekuje się, że to zamknięcie zwróci wynik wyrażenia.
chociaż powyższy przykład kodu używa typów Double
, nie jesteś ograniczony do tych typów. Wynikowy typ funkcji map()
może mieć inny typ niż ten, który do niej wstawisz, i możesz użyć map()
również na funkcji Dictionary
.
przejdźmy do reduce(_:_:)
!
używając funkcji Redukuj
funkcja reduce(_:_:)
zapętla każdy element w kolekcji i zmniejsza go do jednej wartości. Pomyśl o tym jak o połączeniu wielu wartości w jedną.
funkcja reduce jest chyba najtrudniejszą do zrozumienia funkcją map, reduce, filter. Jak można przejść od zbioru wartości do jednej wartości?
kilka przykładów:
- Tworzenie sumy wielu wartości, tj.
3 + 4 + 5 = 12
- łączenie zbioru ciągów, tj.
= "Zaphod, Trillian, Ford"
- uśrednianie zbioru wartości, tj.
(7 + 3 + 10) / 3 = 7/3 + 3/3 + 10/3 = 6.667
w przetwarzaniu danych można sobie wyobrazić wiele scenariuszy, gdy proste operacje takie jak te przydają się. Podobnie jak wcześniej, możesz rozwiązać każdy z tych problemów za pomocą pętli for, ale reduce(_:_:)
jest po prostu krótszy i szybszy.
oto jak:
let sum = values.zmniejsz (0,+)
Drukuj (suma)
funkcja reduce(_:_:)
pobiera dwa argumenty, wartość początkową i zamknięcie. W powyższym kodzie podajemy operator +
, który jest również funkcją o dwóch parametrach.
możesz również podać własne zamknięcie, oczywiście:
let average = values.reduce (0.0) { $0 + $1 } / Double (values.liczyć)
Drukuj (średnio)
w powyższym przykładzie obliczasz średnią z trzech liczb. Typami wartości w kodzie są wszystkie Double
. Najpierw sumujemy wszystkie liczby, a następnie dzielimy je przez ilość liczb.
funkcja reduce(_:_:)
różni się na dwa sposoby:
- funkcja
reduce(_:_:)
ma dwa nienazwane parametry; wartość początkowa i zamknięcie - zamknięcie dostarczone do
reduce(_:_:)
ma również dwa parametry; aktualny wynik redukcji i nowa wartość, która zostanie zmniejszona
tutaj, sprawdź to:
let sum = values.reduce (0) {
Drukuj(„\($0) + \($1) = \($0 + $1)”)
return $0 + $1
}
druk (suma)
w powyższym przykładzie wyraźnie widać i
, 2 parametry zamknięcia. Kiedy uruchamiasz kod, to jest wynik, który otrzymujesz:
0 + 7 = 77 + 3 = 1010 + 10 = 2020
widzisz, jak zaczynamy od 0
, a następnie dodajemy 7
? W następnym kroku bierzemy 7
– aktualną wartość redukcji-i dodajemy 3
,” następną ” wartość redukcji.
oto inny sposób patrzenia na to:
0 + 7(0 + 7) + 3((0 + 7) + 3) + 10
to również wyjaśnia, dlaczego potrzebujesz wartości początkowej dla reduce(_:_:)
, ponieważ jest to pierwszy pierwszy parametr zamknięcia.
redukcja może być trudna do uchwycenia. Ważne jest, aby zrozumieć, że iteracyjnie stosujesz operację, taką jak +
, do zestawu wartości, dopóki nie zostanie Ci tylko jedna wartość. Dosłownie zmniejszasz ilość wartości.
przejdźmy do filter(_:)
!
Korzystanie z funkcji filtrowania
funkcja filter
zapętla każdy element w kolekcji i zwraca kolekcję zawierającą tylko elementy spełniające warunek include.
to tak, jakby zastosować if
-polecenie do kolekcji i zachować tylko wartości, które przechodzą warunek.
zobacz:
let even = values.filter { $0.isMultiple (of: 2) }
print (even)
w powyższym przykładzie filtrujesz liczby z values
, które są parzyste. Funkcja isMultiple(of:)
zwraca true
, gdy można podzielić przez
2
, a false
w przeciwnym razie.
w przeciwieństwie do map(_:)
i reduce(_:_:)
, zamknięcie filter(_:)
musi zwrócić wartość logiczną, więc albo true
, albo false
. Gdy zamknięcie Zwraca wartość true
, wartość jest zachowywana, a gdy zwraca wartość false
, wartość jest pomijana. W ten sposób filter(_:)
filtruje tablicę wejściową.
oto nieco jaśniejszy przykład, z rozszerzonym zamknięciem:
let values = let even = values.filter({ (value:Int) -> Bool in return value.isMultiple(of: 2)})print(even) // Output:
w przykładzie zamknięcie Zwraca wartość logiczną wskazaną przez -> Bool
. Jest dostarczany jeden parametr, element w kolekcji i zwraca wynik isMultiple(of:)
. Super!
łączenie Map, Reduce i Filter
czy możesz połączyć funkcje map()
, reduce()
i filter()
? Oczywiście, że możesz!
powiedzmy, że mamy klasę uczniów. Wiesz, w którym roku urodził się każdy uczeń. Chcesz obliczyć łączny wiek wszystkich uczniów urodzonych w roku 2000 lub później.
oto jak to zrobić:
let years =
let sum = years.filtr({ $0 >= 2000 }).map({ now – $0 }).zmniejsz (0,+)
Drukuj (suma)
powyższa próbka kodu wykorzystuje łańcuch. Wykorzystuje wynik jednej funkcji jako Wejście dla innej, łącząc map-reduce-filter. Funkcja map()
jest wywoływana na tablicy wyników funkcji filter()
, a funkcja reduce()
jest wywoływana na wyniku funkcji map()
. Super!
sam kod jest prosty:
- Utwórz stałą
now
iyears
i przypisz jej kilka lat. - Filtruj lata poniżej 2000, tj. zachowaj te, dla których
>= 2000
jesttrue
- Przekształć każdy rok w wiek, odejmując rok od
now
- Dodaj wszystkie wieki, zmniejszając z
+
weźmy bardziej interesujący przykład. Sprawdź poniższy kod, zaczerpnięty z samouczka o FizzBuzz:
switch (i % 3 == 0, i % 5 == 0)
{
case (true, false):
return „Fizz”
case (false, true):
return „Buzz”
case (true, true):
return „FizzBuzz”
default:
return ” \(i)”
}
}
let result = Array (2…100).Mapa (fizzbuzz).zmniejszyć(„1”, { $0 + „, ” + $1 })
print (wynik)
Zmieniamy tablicę z liczbami od 2 do 100 na „Fizz”, „Buzz” lub „FizzBuzz” z map(_:)
, zgodnie z zasadami gry. Na koniec, redukujemy tę tablicę łańcuchów do jednego dużego łańcucha z reduce(_:_:)
, łącząc każdą wartość. Super!
Czytaj dalej
co by było, gdybyś musiał to wszystko kodować pętlami for in
? Użyłbyś więcej kodu. I to jest map-reduce-filter ’ s power: jest bardziej zwięzły, często łatwiejszy do odczytania, i-przyznaj – cholernie fajny!
chcesz dowiedzieć się więcej? Sprawdź te zasoby:
- jak używać „gdzie” w języku Swift
- Flatmap i CompactMap wyjaśnione w języku Swift
- najlepszy przewodnik po zamknięciach w języku Swift
- jak: znaleźć element w tablicy w języku Swift
- Graj kodem: Wyszukiwanie binarne w języku Swift
- Rozpocznij z Xcode Playgrounds

Zostań programistą iOS
Naucz się tworzyć aplikacje na iOS 14 za pomocą Swift 5
Zapisz się na mój kurs rozwoju iOS i dowiedz się, jak rozpocząć karierę jako profesjonalny programista iOS.