Skrevet av Reinder de Vries 9. juli 2020 i Apputvikling, 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!
- Introduksjon Til Kart, Reduser Og Filtrer
- Hurtigstart: Funksjoner I Høyere Rekkefølge I Swift
- Bruke Kartfunksjonen
- Bruke Reduser-Funksjonen
- Bruke Filterfunksjonen
- Kombinere Kart, Reduser Og Filtrer
- 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 enif
-setning, og bare hvis den betingelsen resulterte itrue
.
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 sommap(_:)
, bortsett fra at det fjernernil
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 entrue
hvis et element tilfredsstiller predikatet, ellersfalse
-
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 somfirst(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 fahrenheit = celsius.kart { $0 * (9/5) + 32 }
print(fahrenheit))
Du kan til og med gjøre alt det på en linje:
.map { * (9/5) + 32 }
Hva skjer her?
- en konstant
celsius
er definert, en rekke dobler og initialisert med noen tilfeldige Celsius-verdier. - funksjonen
map(_:)
kalles påcelsius
– matrisen. Funksjonen har ett argument, en lukning, som konverterer Fra Celsius Til Fahrenheit. - 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 enDouble
parameter som inngang, og forventes å returnere enDouble
. Nå som du har utelattvalue
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 sum = verdier.reduser (0,+)
skriv ut (sum)
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 gjennomsnitt = verdier.redusere(0.0) { $0 + $1 } / Dobbel (verdier.antall)
skriv ut (gjennomsnitt)
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:
- funksjonen
reduce(_:_:)
har to ikke navngitte parametere; den opprinnelige verdien og lukkingen - 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 sum = verdier.reduser (0) {
skriv ut(«\($0) + \($1) = \($0 + $1)»)
tilbake $0 + $1
}
skriv ut (sum)
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 selv = verdier.filter { $0.isMultiple (av: 2)}
skriv ut (selv)
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 år =
la sum = år.filtrer({ $0 >= 2000 }).kart ({ nå – $ 0 }).reduser (0,+)
skriv ut (sum)
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:
- Lag en konstant
now
ogyears
, og tilordne en rekke år til den. - Filtrer ut årene som er under 2000, dvs. hold de som
>= 2000
ertrue
- Forvandle hvert år til en alder, ved å trekke året fra
now
- 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:
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)
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.