napisany przez Reinder de Vries dnia lipca 9 2020 w rozwój aplikacji, Swift

Mapuj, zmniejszaj i filtruj w języku 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!

  1. Wprowadzenie do mapowania, zmniejszania i filtrowania
  2. Szybki Start: Funkcje wyższego rzędu w Swift
  3. Korzystanie z funkcji Map
  4. Korzystanie z funkcji Redukuj
  5. Korzystanie z funkcji Filtruj
  6. łączenie Map, Redukuj i filtruj
  7. 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 co map(_:), tyle, że spłaszcza powstałą sekwencję, tzn. zagnieżdżone tablice są nie zagnieżdżone lub „spłaszczone”
  • compactMap(_:) robi to samo co map(_:), z wyjątkiem tego, że usuwa nil 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, zwraca true, jeśli element spełnia predykat, w przeciwnym razie false
  • 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 co first(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 celsius =
let fahrenheit = celsius.mapka { $0 * (9/5) + 32 }
druk (fahrenheit)
Ukryj Ostrzeżenia

możesz nawet zrobić to wszystko w jednej linii:

.map {  * (9/5) + 32 }

co tu się dzieje?

  1. zdefiniowana jest stała celsius, tablica podwójnych wartości i zainicjalizowana kilkoma losowymi wartościami Celsjusza.
  2. funkcja map(_:) jest wywoływana w tablicy celsius. Funkcja ma jeden argument, zamknięcie, które konwertuje z Celsjusza na Fahrenheita.
  3. 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 parametru Doublejako wejścia i oczekuje się, że zwróci Double. 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 values =
let sum = values.zmniejsz (0,+)
Drukuj (suma)
Ukryj Ostrzeżenia

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 values =
let average = values.reduce (0.0) { $0 + $1 } / Double (values.liczyć)
Drukuj (średnio)
Ukryj Ostrzeżenia

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:

  1. funkcja reduce(_:_:) ma dwa nienazwane parametry; wartość początkowa i zamknięcie
  2. zamknięcie dostarczone do reduce(_:_:) ma również dwa parametry; aktualny wynik redukcji i nowa wartość, która zostanie zmniejszona

tutaj, sprawdź to:

let values =
let sum = values.reduce (0) {
Drukuj(„\($0) + \($1) = \($0 + $1)”)
return $0 + $1
}
druk (suma)
Ukryj Ostrzeżenia

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 values =
let even = values.filter { $0.isMultiple (of: 2) }
print (even)
Ukryj Ostrzeżenia

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 now = 2020
let years =
let sum = years.filtr({ $0 >= 2000 }).map({ now – $0 }).zmniejsz (0,+)
Drukuj (suma)
Ukryj Ostrzeżenia

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:

  1. Utwórz stałą now i years i przypisz jej kilka lat.
  2. Filtruj lata poniżej 2000, tj. zachowaj te, dla których >= 2000 jest true
  3. Przekształć każdy rok w wiek, odejmując rok od now
  4. Dodaj wszystkie wieki, zmniejszając z +

weźmy bardziej interesujący przykład. Sprawdź poniższy kod, zaczerpnięty z samouczka o FizzBuzz:

let fizzbuzz: (Int) – > String = { i in
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)
Ukryj Ostrzeżenia

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.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.