scris de Reinder de Vries pe 9 iulie 2020 în dezvoltarea aplicațiilor, Swift

harta, Reduce și se filtrează în Swift

în Swift utilizați map(), reduce() și filter() la bucla peste colecții, cum ar fi matrice și dicționare, fără a utiliza un for-buclă.

funcțiile de hartă, reducere și filtrare provin din domeniul programării funcționale (FP). Sunt numite funcții de ordin superior, deoarece iau funcții ca intrare. Aplicați o funcție unui tablou, de exemplu, pentru a-i transforma datele.

harta Swift, Reduce și funcțiile de filtrare pot provocare să-și încheie capul în jurul valorii de. Mai ales dacă ați codat întotdeauna for in bucle pentru a rezolva problemele de iterație. În acest ghid veți învăța cum să utilizați funcțiile map(_:), reduce(_:_:) și filter(_:) în Swift.

să începem!

  1. Introducere în hartă, reduceți și filtrați
  2. pornire rapidă: Funcții de ordin superior în Swift
  3. utilizarea funcției de hartă
  4. utilizarea funcției de reducere
  5. utilizarea funcției de filtrare
  6. combinarea hărții, reducerii și filtrării
  7. lecturi suplimentare

Introducere în hartă, reducere și filtrare

când construiți iOS Aplicații, utilizați de obicei programare procedurală sau orientată pe obiecte. Programarea funcțională este diferită: se ocupă doar de funcții. Nu există variabile, nici o stare, nu pentru-bucle — doar funcții.

limbajul de programare Swift se pretează perfect pentru amestecarea programării funcționale cu abordări nefuncționale, cum ar fi OOP. Nu este nevoie să scrieți strict cod funcțional, iar adoptarea conceptelor din programarea funcțională vă poate ajuta să învățați cum să codificați mai bine.

funcțiile map(_:), reduce(_:_:) și filter(_:) sunt numite funcții de ordin superior, deoarece iau o funcție ca funcții de intrare și retur ca ieșire. Din punct de vedere tehnic, Swift returnează rezultatele unei operațiuni (adică. o matrice transformată) atunci când se utilizează funcții de ordin superior, în timp ce un limbaj funcțional pur va returna o colecție de funcții. În Swift, intrările pentru aceste funcții sunt închideri.

Iată cum funcționează:

  • funcția map() aplică o funcție fiecărui element dintr-o colecție. Gândiți-vă la „maparea” sau transformarea unui set de valori într-un alt set de valori.
  • funcția reduce() transformă o colecție într-o singură valoare. Gândiți-vă la aceasta ca la combinarea multor valori într-una, cum ar fi o medie a unui set de numere.
  • funcția filter() returnează pur și simplu valori care au trecut o declarație if-și numai dacă această condiție a dus la true.

în cazul în care vă gândiți: „uite, nu am nevoie de programare funcțională sau de prelucrare a datelor, deoarece aplicațiile mele nu fac asta!”atunci nu te opri aici. În proiectele recente de aplicații am folosit harta, reducerea și filtrarea în mai multe ocazii:

  • filtrarea valorilor costurilor / veniturilor cu filter(_:) , pentru a atinge un prag, înainte de a afișa valorile într-un grafic liniar
  • Media a mii de evaluări ale filmelor într-o singură valoare cu reduce(_:_:)
  • maparea câtorva operații pe un șir cu hashtag-uri, transformându-l într-o colecție normalizată, cu map(_:)

ați fi putut rezolva toate aceste probleme cu un for-loop, dar veți vedea că utilizarea map(), reduce() și filter() are ca rezultat un cod mai concis, mai lizibil și mai performant.

Pornire Rapidă: Funcții de ordin superior în Swift

ne vom concentra pe map(), reduce() și filter() în acest tutorial. Înainte de a merge mai departe, iată o prezentare rapidă a celor mai comune funcții de ordin superior din Swift:

  • map(_:) bucle peste fiecare element dintr-o secvență, aplică o funcție fiecărui element și returnează rezultatul transformat
  • reduce(_:_:) bucle peste fiecare element dintr-o secvență, le combină într-o singură valoare și returnează rezultatul combinat
  • filter(_:) bucle peste fiecare element dintr-o secvență și returnează o secvență rezultată care conține doar elemente care satisfac o funcție de filtrare dată
  • flatMap(_:) face același lucru cu map(_:), cu excepția faptului că aplatizează secvența rezultată, adică. matricele imbricate sunt ne-imbricate sau „aplatizate”
  • compactMap(_:) face același lucru ca și map(_:), cu excepția faptului că elimină nil valori din secvența rezultată înainte de a se întoarce

puteți utiliza aceste funcții pe matrice, dicționare, seturi, intervale, secvențe, și orice alt tip Swift puteți itera peste. Dacă doriți să aflați mai multe despre compactMap(_:) și flatMap(_:), consultați acest tutorial.

mai multe tipuri în Swift, cum ar fi matrice și Dicționar, au funcții care acceptă închideri ca intrare. O alegere rapidă:

  • contains(where:) bucle peste o colecție, aplică un predicat (o închidere) fiecărui element, returnează un true dacă un element satisface predicatul, altfel false
  • first(where:) bucle peste o colecție, se aplică un predicat (o închidere) pentru fiecare element, și returnează elementul în cazul în care satisface predicat
  • firstIndex(where:) face același lucru ca first(where:), cu excepția faptului că returnează indicele în loc de valoarea

puteți afla mai multe despre aceste funcții în acest tutorial. Cum where este folosit în Swift este interesant, de asemenea, puteți afla mai multe despre asta în acest tutorial.

ați văzut funcțiile „hartă” și „reducere” în Swift scrise ca map(_:) și reduce(_:_:) pe parcursul acestui tutorial. Sublinierile și punctele din aceste funcții fac parte din semnătura funcției, care este un format special pentru a indica parametrii funcției. De exemplu, funcția map(_:) are un parametru fără nume, în timp ce funcția reduce(_:_:) are două. Puteți afla mai multe despre asta în acest tutorial: funcții în Swift explicate.

angajați-vă ca dezvoltator iOS

Aflați cum să construiți Aplicații iOS 14 cu Swift 5

Înscrieți-vă la cursul meu de dezvoltare iOS și aflați cum să vă începeți cariera ca dezvoltator iOS profesionist.

utilizarea funcției Map

funcția map(_:) se suprapune peste fiecare element dintr-o colecție și aplică o operație fiecărui element din colecție. Returnează o colecție de articole rezultate, la care a fost aplicată operațiunea.

să ne uităm la un exemplu. Avem o serie de temperaturi în grade Celsius pe care doriți să le transformați în Fahrenheit.

ai putea folosi un for-loop, ca aceasta:

let celsius = var fahrenheit: = for value in celsius { fahrenheit += }print(fahrenheit)// Output: 

codul funcționează bine, dar este prea detaliat. Aveți nevoie de o variabilă „helper” mutabilă fahrenheit pentru a stoca conversiile calculate pe măsură ce lucrați prin ele și aveți nevoie de 3 linii de cod pentru Conversia în sine.

Iată cum putem face același lucru cu funcția map(_:) :

fie celsius =
fie fahrenheit = celsius.hartă { $0 * (9/5) + 32 }
print (fahrenheit)
ascunde avertismente

ai putea face chiar toate că pe o singură linie:

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

ce se întâmplă aici?

  1. este definită o constantă celsius, o serie de duble și inițializată cu câteva valori Celsius aleatorii.
  2. funcția map(_:) este apelată pe matricea celsius. Funcția are un argument, o închidere, care se transformă din Celsius în Fahrenheit.
  3. în cele din urmă, rezultatul este tipărit: matricea convertită, de la Celsius la Fahrenheit.

funcția map(_:) transformă o matrice în alta, aplicând o funcție fiecărui element din matrice. Închiderea * (9/5) + 32 ia valoarea de intrare în Celsius și returnează o valoare în Fahrenheit. Matricea rezultată de map(_:) este construită din acele valori convertite.

să aruncăm o privire mai atentă la închidere. Dacă ați mai lucrat cu închideri, veți recunoaște sintaxa de închidere cu mână scurtă. Este un mod mai scurt de a codifica o închidere, lăsând o mare parte din sintaxa sa.

Iată o alternativă mai puțin concisă:

let celsius = let fahrenheit = celsius.map({ (value: Double) -> Double in return value * (9/5) + 32})print(fahrenheit)

real map(_:) funcția de apel, și închiderea acestuia, este aceasta:

··· = celsius.map({ (value: Double) -> Double in return value * (9/5) + 32})

ce se întâmplă acolo? Funcția map(_:) este apelată pe matrice celsius. Este nevoie de un argument: o închidere de tip (Double) -> Double.

prima parte a închiderii, începând cu {, indică faptul că această Închidere are un parametru value de tip Double, iar închiderea returnează o valoare de tip Double. Corpul de închidere, începând cu return, returnează pur și simplu rezultatul calculului Celsius la Fahrenheit.

dacă comparați sintaxa de închidere cu mâna scurtă cu codul extins de mai sus, veți vedea că:

  • parantezele funcției ( și ) sunt omise, deoarece le puteți lăsa afară atunci când ultimul parametru al unui apel de funcție este o închidere.
  • partea () -> in poate fi omisă, deoarece Swift poate deduce că utilizați un parametru Doubleca intrare și se așteaptă să returneze un Double. Acum că ați omis variabila value, puteți utiliza mâna scurtă .
  • declarația return poate fi lăsată deoparte, deoarece această închidere este de așteptat să returneze rezultatul unei expresii oricum.

chiar dacă eșantionul de cod de mai sus utilizează tipuri Double, nu vă limitați la aceste tipuri. Tipul rezultat al unei funcții map() poate avea un tip diferit de ceea ce ați pus în ea și puteți utiliza map() și pe un Dictionary.

să trecem la reduce(_:_:)!

folosind funcția de reducere

funcția reduce(_:_:) se suprapune peste fiecare element dintr-o colecție și le reduce la o singură valoare. Gândiți-vă la aceasta ca la combinarea mai multor valori într-una.

funcția reduce este, probabil, cel mai greu de hartă, reduce, filtru pentru a înțelege. Cum poți trece de la o colecție de valori, la o singură valoare?

câteva exemple:

  • crearea unei sume de valori multiple, adică 3 + 4 + 5 = 12
  • concatenarea unei colecții de șiruri, adică = "Zaphod, Trillian, Ford"
  • medierea unui set de valori, adică.(7 + 3 + 10) / 3 = 7/3 + 3/3 + 10/3 = 6.667

în procesarea datelor, vă puteți imagina o mulțime de scenarii atunci când operațiunile simple ca acestea vin la îndemână. Ca și înainte, puteți rezolva oricare dintre aceste probleme cu o buclă for, dar reduce(_:_:) este pur și simplu mai scurtă și mai rapidă.

Iată cum:

fie valori =
fie sumă = valori.reduce (0, +)
imprimare (sumă)
ascunde avertismente

funcția reduce(_:_:) ia două argumente, o valoare inițială și o închidere. În codul de mai sus furnizăm operatorul +, care este, de asemenea, o funcție cu doi parametri.

puteți oferi, de asemenea, propria închidere, desigur:

să valori =
să medie = valori.reduce (0.0) { $0 + $1 } / dublu (valori.număr)
imprimare(medie)
ascundeți avertismentele

în exemplul de mai sus, calculați media a trei numere. Tipurile de Valori din cod sunt toate Double. Mai întâi adunăm toate numerele și apoi le împărțim la numărul de numere.

funcția reduce(_:_:) este diferită în două moduri:

  1. funcția reduce(_:_:) are doi parametri fără nume; valoarea inițială și închiderea
  2. închiderea furnizată reduce(_:_:) are, de asemenea, doi parametri; rezultatul actual al reducerii, și noua valoare care este pe cale de a obține redus

aici, a verifica acest lucru:

fie valori =
fie sumă = valori.reduce(0) {
imprimare(„\($0) + \($1) = \($0 + $1)”)
întoarcere $0 + $1
}
print (sumă)
ascundeți avertismentele

în exemplul de mai sus, puteți vedea clar și , cei 2 parametri ai închiderilor. Când executați codul, aceasta este ieșirea pe care o obțineți:

0 + 7 = 77 + 3 = 1010 + 10 = 2020

vedeți cum începem cu 0, apoi adăugăm 7? În pasul următor, luăm 7 – valoarea curentă de reducere – și adăugăm 3, valoarea „următoare” din reducere.

Iată un alt mod de a privi lucrurile:

0 + 7(0 + 7) + 3((0 + 7) + 3) + 10

acest lucru arată, de asemenea, clar de ce aveți nevoie de o valoare inițială pentru reduce(_:_:), deoarece acesta este primul parametru al închiderii.

reducerea poate fi dificil de înțeles. Este important să înțelegeți că aplicați iterativ o operație, cum ar fi +, unui set de valori până când rămâneți cu o singură valoare. Reduceți literalmente cantitatea de valori.

să trecem la filter(_:)!

utilizarea funcției filtru

funcția filter se suprapune peste fiecare element dintr-o colecție și returnează o colecție care conține numai elemente care îndeplinesc o condiție includ.

este ca și cum ai aplica o declarație if -unei colecții și ai păstra doar valorile care trec condiția.

aici, a verifica acest lucru:

fie valori =
fie chiar = valori.filtru { $0.isMultiple (de: 2)}
imprimare (chiar)
ascundeți avertismentele

în exemplul de mai sus, filtrați numere din values care sunt pare. Funcția isMultiple(of:) returnează true când poate fi împărțită la 2 și false în caz contrar.

spre deosebire de map(_:) și reduce(_:_:), închiderea filter(_:) trebuie să returneze un boolean, deci fie true, fie false. Când închiderea returnează true, valoarea este păstrată, iar când false este returnată, valoarea este omisă. Acesta este modul în care filter(_:) filtrează matricea de intrare.

Iată un exemplu ușor mai clar, cu închiderea extinsă:

let values = let even = values.filter({ (value:Int) -> Bool in return value.isMultiple(of: 2)})print(even) // Output: 

în exemplu, închiderea returnează o valoare booleană, indicată cu -> Bool. Este furnizat un parametru, elementul din colecție, și returnează rezultatul isMultiple(of:). Neat!

combinarea hărții, reducerea și filtrarea

puteți combina funcțiile map(), reduce() și filter()? Sigur că poți!

să spunem că avem o clasă de studenți. Știți anul în care s-a născut fiecare elev. Doriți să calculați vârsta combinată a tuturor studenților născuți în sau după anul 2000.

Iată cum faci asta:

să Acum = 2020
să Ani =
să sumă = ani.filtru({ $0 >= 2000 }).hartă ({ Acum – $0}).reduce (0, +)
imprimare (sumă)
ascundeți avertismentele

eșantionul de cod de mai sus folosește înlănțuirea. Acesta utilizează rezultatul unei funcții ca intrare pentru alta, combinând harta-reduce-filtru. Funcția map() este apelată pe matricea de rezultate a funcției filter(), iar funcția reduce() este apelată pe rezultatul funcției map(). Minunat!

codul în sine este simplu:

  1. faceți o constantă now și years și alocați-i o grămadă de ani.
  2. filtrați anii care sunt sub 2000, adică. păstrați cele pentru care >= 2000 este true
  3. transformați în fiecare an într-o epocă, scăzând anul de la now
  4. adăugați toate vârstele, reducând cu +

să luăm un exemplu mai interesant. Consultați următorul cod, preluat din tutorialul despre FizzBuzz:

să fizzbuzz: (Int) – > String = { i în
comutator (i % 3 = = 0, i % 5 == 0)
{
case (true, false):
return „Fizz”
case (false, true):
return „Buzz”
case (true, true):
return „FizzBuzz”
default:
întoarcere ” \ (i)”
}
}
să rezultat = matrice (2…100).hartă (fizzbuzz).reduce(„1”, { $0 + „, ” + $1 })
imprimare (rezultat)
ascunde avertismente

vezi ce se întâmplă? Transformăm o matrice cu numere de la 2 la 100 în „Fizz”, „Buzz” sau „FizzBuzz” cu map(_:), pe baza regulilor jocului. În cele din urmă, reducem acea matrice de șiruri într-un șir mare cu reduce(_:_:), combinând fiecare valoare. Neat!

lecturi suplimentare

ce se întâmplă dacă ar trebui să codificați toate acestea cu for in bucle? Ai folosi mult mai mult cod. Și asta e puterea map-reduce-filter: este mai concis, de multe ori mai ușor de citit, și — recunosc — destul de al naibii de cool!

vrei să afli mai multe? Verificați aceste resurse:

  • cum să utilizați” unde ” în Swift
  • FlatMap și CompactMap explicate în Swift
  • ghidul final pentru închideri în Swift
  • cum să: găsiți un element într-o matrice în Swift
  • jucați cu cod: Căutare binară în Swift
  • începeți cu Xcode Playgrounds

angajați-vă ca dezvoltator iOS

Aflați cum să construiți Aplicații iOS 14 cu Swift 5

Înscrieți-vă la cursul meu de dezvoltare iOS și aflați cum să vă începeți cariera ca dezvoltator iOS profesionist.

Lasă un răspuns

Adresa ta de email nu va fi publicată.