Skrevet av Reinder de Vries 9. juli 2020 i Apputvikling, Swift

Kart, Reduser og Filtrer I Swift

I Swift bruker du map(), reduce() og filter() for å sløyfe over samlinger som matriser og ordbøker, uten å bruke en for-loop.

kart -, reduksjons-og filterfunksjonene kommer fra riket av funksjonell programmering (fp). De kalles høyere ordensfunksjoner, fordi de tar funksjoner som input. Du bruker en funksjon til en matrise, for eksempel for å transformere dataene.

Swifts Kart -, Reduksjons-og Filterfunksjoner kan utfordre å vikle hodet rundt. Spesielt hvis du alltid har kodet for in looper for å løse iterasjonsproblemer. I denne veiledningen lærer du hvordan Du bruker Funksjonene map(_:), reduce(_:_:) og filter(_:) I Swift.

La oss komme i gang!

  1. Introduksjon Til Kart, Reduser Og Filtrer
  2. Hurtigstart: Funksjoner I Høyere Rekkefølge I Swift
  3. Bruke Kartfunksjonen
  4. Bruke Reduser-Funksjonen
  5. Bruke Filterfunksjonen
  6. Kombinere Kart, Reduser Og Filtrer
  7. Videre Lesing

Introduksjon Til Kart, Reduser Og Filtrer

Når du bygger iOS apper, Bruker Du Vanligvis Prosedyremessig Eller Objektorientert Programmering. Funksjonell programmering er forskjellig: det handler bare om funksjoner. Ingen variabler, ingen tilstand, ingen for-loops-bare funksjoner.

Swift programmeringsspråk egner seg perfekt for å blande funksjonell programmering med ikke-funksjonelle tilnærminger, som OOP. Du trenger ikke å strengt skrive funksjonell kode, og vedta konsepter fra funksjonell programmering kan hjelpe deg å lære å kode bedre.

funksjonene map(_:), reduce(_:_:) og filter(_:) kalles høyere ordensfunksjoner, fordi de tar en funksjon som inngangs-og returfunksjoner som utgang. Teknisk Returnerer Swift resultatene av en operasjon (dvs. en transformert matrise) når du bruker høyere ordensfunksjoner, mens et rent funksjonelt språk vil returnere en samling funksjoner. I Swift er inngangene for disse funksjonene lukninger.

slik fungerer de:

  • funksjonen map() gjelder en funksjon for hvert element i en samling. Tenk på «kartlegging» eller transformere ett sett med verdier til et annet sett med verdier.
  • funksjonen reduce() gjør en samling om til en verdi. Tenk på det som å kombinere mange verdier i en, som gjennomsnitt et sett med tall.
  • funksjonen filter() returnerer bare verdier som passerte en if-setning, og bare hvis den betingelsen resulterte i true.

hvis du tenker: «Se, Jeg trenger ikke funksjonell programmering eller databehandling, fordi appene mine ikke gjør det!»så ikke stopp her. I nyere appprosjekter har jeg brukt Kart, Redusere Og Filtrere ved flere anledninger:

  • Filtrer kostnads – /inntektsverdier med filter(_:), for å møte en terskel, før du viser verdiene i en linjediagram
  • Gjennomsnitt tusenvis av filmkarakterer til en verdi med reduce(_:_:)
  • Kartlegger noen få operasjoner på en streng med hashtags, forvandler den til en normalisert samling, med map(_:)

Du kunne ha løst alle disse problemene med en for-loop, men du vil se at bruk av map(), reduce() og filter() resulterer i mer kortfattet, lesbar og performant kode.

Hurtigstart: Høyere Ordensfunksjoner I Swift

vi fokuserer på map(), reduce() og filter() i denne opplæringen. Før Vi går videre, er det en rask oversikt over De vanligste høyere ordensfunksjonene I Swift:

  • map(_:) looper over hvert element i en sekvens, bruker en funksjon på hvert element, og returnerer det transformerte resultatet
  • reduce(_:_:) looper over hvert element i en sekvens, kombinerer dem til en verdi, og returnerer det kombinerte resultatet
  • flatMap(_:) gjør det samme sommap(_:), bortsett fra at den flater den resulterende sekvensen, dvs. nestede arrays er un-nested eller «flatet ut»
  • compactMap(_:) gjør det samme som map(_:), bortsett fra at det fjerner nil verdier fra den resulterende sekvensen før du returnerer den

Du kan bruke disse funksjonene på matriser, ordbøker, sett, områder, sekvenser og andre Swift-typer du kan iterere over. Hvis du vil lære mer om compactMap(_:) og flatMap(_:), så sjekk ut denne opplæringen.

Flere Typer I Swift, For Eksempel Array og Dictionary, har funksjoner som godtar nedleggelser som inndata. En rask plukke:

  • contains(where:) looper over en samling, bruker et predikat (en lukning) til hvert element, returnerer en true hvis et element tilfredsstiller predikatet, ellers false
  • first(where:) looper over en samling, bruker et predikat (en lukning) til hvert element, og returnerer elementet hvis det tilfredsstiller predikatet
  • firstIndex(where:) gjør det samme som first(where:), bortsett fra at det returnerer indeksen i stedet for verdien

du kan lære mer om disse funksjonene i denne opplæringen. Hvordan where brukes I Swift er også interessant, du kan lære mer om det i denne opplæringen.

Du har sett funksjonene «kart» og «reduser» I Swift skrevet som map(_:) og reduce(_:_:) gjennom denne opplæringen. Understrek og kolon i disse funksjonene er en del av funksjonens signatur, som er et spesielt format for å indikere funksjonsparametere. Funksjonen map(_:) har for eksempel en ikke navngitt parameter, mens funksjonen reduce(_:_:) har to. Du kan lære mer om Det i denne opplæringen: Funksjoner I Swift Forklart.

Bli ansatt som iOS-utvikler

Lær å bygge iOS 14-apper med Swift 5

Registrer deg for mitt iOS-utviklingskurs, og lær hvordan du starter din karriere som profesjonell iOS-utvikler.

Bruke Kartfunksjonen

funksjonen map(_:) går over hvert element I en samling, og bruker en operasjon på hvert element i samlingen. Den returnerer en samling av resulterende elementer, som operasjonen ble brukt på.

La oss se på et eksempel. Vi har en rekke temperaturer I Celsius som du vil forvandle Til Fahrenheit.

Du kan bruke en for-loop, som dette:

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

koden fungerer fint, men det er for verbose. Du trenger en foranderlig «hjelper» variabel fahrenheit for å lagre de beregnede konverteringene mens du arbeider gjennom dem, og du trenger 3 linjer med kode for selve konverteringen.

slik kan vi gjøre det samme med map(_:) – funksjonen:

la celsius =
la fahrenheit = celsius.kart { $0 * (9/5) + 32 }
print(fahrenheit))
Skjul advarsler

Du kan til og med gjøre alt det på en linje:

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

Hva skjer her?

  1. en konstant celsius er definert, en rekke dobler og initialisert med noen tilfeldige Celsius-verdier.
  2. funksjonen map(_:) kalles på celsius – matrisen. Funksjonen har ett argument, en lukning, som konverterer Fra Celsius Til Fahrenheit.
  3. Til Slutt skrives resultatet ut: det konverterte arrayet, Fra Celsius Til Fahrenheit.

funksjonen map(_:) forvandler en matrise til en annen ved å bruke en funksjon på hvert element i matrisen. Lukkingen * (9/5) + 32 tar inndataverdien I Celsius, og returnerer en verdi I Fahrenheit. Den resulterende matrisen map(_:) er bygget opp av de konverterte verdiene.

La oss ta en nærmere titt på nedleggelsen. Hvis du har jobbet med nedleggelser før, vil du gjenkjenne korthåndssyntaksen. Det er en kortere måte å kode en nedleggelse, ved å utelate mye av sin syntaks.

Her er et mindre kortfattet alternativ:

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

den faktiske map(_:) funksjonskallet, og dens lukking, er dette:

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

Hva skjer der? Funksjonen map(_:) kalles på matrisen celsius. Det tar ett argument: en lukning av typen (Double) -> Double.

den første delen av lukkingen, som starter med {, indikerer at denne lukkingen har en parameter value av typen Double, og lukkingen returnerer en verdi av typen Double. Lukkekroppen, som starter med return, returnerer bare Resultatet Av Celsius Til fahrenheit-beregningen.

hvis du sammenligner korthåndssyntaksen med den utvidede koden ovenfor, ser du det:

  • funksjonsparentesene ( og ) utelates, fordi du kan utelate dem når den siste parameteren i et funksjonskall er en avslutning.
  • delen () -> in kan utelates, Fordi Swift kan utlede at du bruker en Double parameter som inngang, og forventes å returnere en Double. Nå som du har utelatt value variabelen, kan du bruke korthånden .
  • return – setningen kan også utelates, fordi denne lukkingen forventes å returnere resultatet av et uttrykk uansett.

selv om kodeeksemplet ovenfor bruker Double – typer, er du ikke begrenset til disse typene. Den resulterende typen av en map() – funksjon kan ha en annen type enn hva du legger inn i den, og du kan også bruke map() på en Dictionary.

La oss gå videre til reduce(_:_:)!

Bruke Reduce-Funksjonen

funksjonen reduce(_:_:) går over hvert element i en samling, og reduserer Dem til en verdi. Tenk på det som å kombinere flere verdier til en.

reduser-funksjonen er kanskje den vanskeligste av kart, reduser, filtrer for å forstå. Hvordan kan du gå fra en samling av verdier, til en verdi?

noen eksempler:

  • Opprette en sum av flere verdier, dvs. 3 + 4 + 5 = 12
  • Sammenkjede en samling av strenger, dvs. = "Zaphod, Trillian, Ford"
  • Gjennomsnitt et sett med verdier, dvs.(7 + 3 + 10) / 3 = 7/3 + 3/3 + 10/3 = 6.667

i databehandling kan du forestille deg mange scenarier når enkle operasjoner som disse kommer til nytte. Som før kan du løse noen av disse problemene med en for-loop, men reduce(_:_:) er bare kortere og raskere.

Her er hvordan:

la verdier =
la sum = verdier.reduser (0,+)
skriv ut (sum)
Skjul advarsler

funksjonen reduce(_:_:) tar to argumenter, en startverdi og en lukning. I koden ovenfor gir vi operatøren +, det er også en funksjon med to parametere.

du kan også gi din egen nedleggelse, selvfølgelig:

la verdier =
la gjennomsnitt = verdier.redusere(0.0) { $0 + $1 } / Dobbel (verdier.antall)
skriv ut (gjennomsnitt)
Skjul advarsler

i eksemplet ovenfor beregner du gjennomsnittet av tre tall. Typene av verdiene i koden er alle Double. Vi legger først opp alle tallene, og deler det deretter med antall tall.

funksjonen reduce(_:_:) er forskjellig på to måter:

  1. funksjonen reduce(_:_:) har to ikke navngitte parametere; den opprinnelige verdien og lukkingen
  2. lukkingen som leveres til reduce(_:_:), har også to parametere; det nåværende resultatet av reduksjonen, og den nye verdien som skal reduseres

her, sjekk dette ut:

la verdier =
la sum = verdier.reduser (0) {
skriv ut(«\($0) + \($1) = \($0 + $1)»)
tilbake $0 + $1
}
skriv ut (sum)
Skjul advarsler

i eksemplet ovenfor kan du tydelig se og , de 2 parametrene til lukningene. Når du kjører koden, er dette utgangen du får:

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

Se hvordan vi starter med 0 , og deretter legge til 7? I neste trinn tar vi 7 – den nåværende reduksjonsverdien – og legger til 3,» neste » – verdien i reduksjonen.

her er en annen måte å se på det:

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

Dette gjør det også klart hvorfor du trenger en startverdi for reduce(_:_:), fordi det er den første første parameteren til lukkingen.

Reduksjon kan være vanskelig å forstå. Det er viktig å forstå at du iterativt bruker en operasjon, som +, til et sett med verdier til du er igjen med bare en verdi. Du reduserer bokstavelig talt mengden verdier.

La oss gå videre til filter(_:)!

Ved Hjelp Av Filterfunksjonen

funksjonen filter går over hvert element i en samling, og returnerer en samling som bare inneholder elementer som oppfyller en inkluder-betingelse.

Det er som å bruke en if-setning til en samling, og bare beholde verdiene som passerer betingelsen.

her, sjekk dette ut:

la verdier =
la selv = verdier.filter { $0.isMultiple (av: 2)}
skriv ut (selv)
Skjul advarsler

i eksemplet ovenfor filtrerer du tall fra values som er like. Funksjonen isMultiple(of:) returnerer true når kan deles på 2 og false ellers.

I Motsetning til map(_:) og reduce(_:_:), må nedleggelsen filter(_:) returnere en boolsk, så enten true eller false. Når lukkingen returnerer true, beholdes verdien, og når false returneres, utelates verdien. Det er slik filter(_:) filtrerer inngangsarrayen.

Her er et litt klarere eksempel, med lukkingen utvidet:

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

i eksemplet returnerer lukkingen en boolsk verdi, angitt med -> Bool. Det er gitt en parameter, elementet i samlingen, og returnerer resultatet av isMultiple(of:). Ryddig!

Kombinere Kart, Reduser Og Filtrer

kan du kombinere funksjonene map(), reduce() og filter()? Klart du kan!

La oss si at vi har en klasse av studenter. Du vet året hver student ble født. Du vil beregne den samlede alderen til alle studenter født i eller etter 2000.

Slik gjør du det:

la nå = 2020
la år =
la sum = år.filtrer({ $0 >= 2000 }).kart ({ nå – $ 0 }).reduser (0,+)
skriv ut (sum)
Skjul advarsler

kodeeksemplet ovenfor bruker kjeding. Den bruker resultatet av en funksjon som input for en annen, kombinere kart-redusere-filter. Funksjonen map() kalles på resultatet av funksjonen filter(), og funksjonen reduce() kalles på resultatet av funksjonen map(). Fantastisk!

selve koden er enkel:

  1. Lag en konstant now og years, og tilordne en rekke år til den.
  2. Filtrer ut årene som er under 2000, dvs. hold de som >= 2000 er true
  3. Forvandle hvert år til en alder, ved å trekke året fra now
  4. Legg alle aldre opp, ved å redusere med +

La oss ta et mer interessant eksempel. Sjekk ut følgende kode, hentet fra opplæringen Om FizzBuzz:

la fizzbuzz: (Int) -> Streng = { i i
bryter (i % 3 = = 0, jeg % 5 == 0)
{
case (true, false):
returner «Fizz»
case (false, true):
returner «Buzz»
case (true, true):
returner «FizzBuzz»
standard:
retur «\(i)»
}
}
la resultat = Array (2…100).kart(fizzbuzz).redusere(«1», { $0 + «, » + $1 })
skriv ut (resultat)
Skjul advarsler

Se hva som skjer? Vi forvandler en matrise med tall fra 2 til 100 til enten «Fizz», «Buzz» eller «FizzBuzz» med map(_:), basert på spillets regler. Til slutt reduserer vi den rekke strenger til en stor streng med reduce(_:_:), som kombinerer hver verdi. Ryddig!

Videre Lesing

Hva om du måtte kode alt dette med for in sløyfer? Du vil bruke mye mer kode. Og det er kart-redusere-filter makt: det er mer konsis, ofte lettere å lese — og-innrømme det-ganske jævla kult!

Vil du lære mer? Sjekk ut disse ressursene:

  • Slik Bruker du «hvor» I Swift
  • FlatMap Og CompactMap Forklart I Swift
  • Den Ultimate Veiledningen Til Nedleggelser I Swift
  • Slik Finner Du Et Element I En Matrise I Swift
  • Spill Med Kode: Binært Søk I Swift
  • Kom I Gang Med Xcode Playgrounds

Bli ansatt som iOS-utvikler

Lær å bygge iOS 14-apper med Swift 5

Registrer deg for mitt iOS-utviklingskurs, og lær hvordan du starter din karriere som profesjonell iOS-utvikler.

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert.