I forrige artikkel undersøkte vi grunnleggende funksjonene i Swift. Funksjoner har imidlertid mye mer å tilby. I denne artikkelen fortsetter vi utforskningen av funksjoner og ser på funksjonsparametere, nesting og typer.
La oss se på et av eksemplene fra forrige artikkel. De printMessage (melding :)
funksjon definerer en parameter, budskap
.
func printMessage (melding: String) print (message)
Vi tildeler et navn, budskap
, til parameteren og bruk dette navnet når vi ringer til funksjonen.
printMessage (melding: "Hei, verden!")
Men legg merke til at vi også bruker samme navn for å referere til verdien av parameteren i funksjonens kropp.
func printMessage (melding: String) print (message)
I Swift har en parameter alltid a lokal parameternavn, og det har eventuelt en utvendig parameternavn. I eksemplet er de lokale og eksterne parameternavnene identiske.
Fra Swift 3 har Swift-teamet definert et klart sett med API-retningslinjer. Jeg vil ikke gå inn i disse retningslinjene i denne opplæringen, men jeg vil påpeke at definisjonen av printMessage (melding :)
Funksjonen avviker fra disse retningslinjene. Navnet på funksjonen inneholder ordet budskap
, og parameteren heter også budskap
. Med andre ord, gjentar vi oss selv.
Det ville være mer elegant hvis vi kunne påberope seg printMessage (melding :)
funksjon uten budskap
søkeord. Dette er det jeg har i tankene.
printMessage ("Hei, verden!")
Dette er mulig, og det er mer i tråd med retningslinjene for Swift API. Men hva er forskjellig? Forskjellen er lett å se hvis vi tar en titt på den oppdaterte funksjonsdefinisjonen. Det oppdaterte eksemplet avslører også mer om funksjonalitetens anatomi i Swift.
func printMessage (_ message: String) print (message)
I en funksjonsdefinisjon defineres hver parameter av et eksternt parameternavn, et lokalt parameternavn, et kolon og typen av parameteren. Hvis de lokale og eksterne parameternavnene er identiske, skriver vi bare parameternavnet en gang. Derfor definerer det første eksemplet ett parameternavn, budskap
.
Hvis vi ikke vil tildele et eksternt parameternavn til en parameter, bruker vi _
, et understrek. Dette informerer kompilatoren om at parameteren ikke har et eksternt parameternavn, og det betyr at vi kan utelate parameternavnet når funksjonen påberopes.
Mål-C er kjent for sine lange metodenavn. Selv om dette kan se clunky og inelegant til utenforstående, gjør det metoder enkle å forstå og, hvis det vel velges, veldig beskrivende. Swift-teamet forstod denne fordelen og introduserte eksterne parameternavn fra dag ett.
Når en funksjon aksepterer flere parametere, er det ikke alltid klart hvilket argument som tilsvarer hvilken parameter. Ta en titt på følgende eksempel for å bedre forstå problemet. Legg merke til at parameterne ikke har et eksternt parameternavn.
func power (_a: Int, _b: Int) -> Int var resultat = a for _ i 1 ...De
makt(_:_:)
funksjonen øker verdien aven
av eksponentenb
. Begge parameterne er av typenint
. Mens de fleste vil intuitivt passere basisverdien som det første argumentet og eksponenten som det andre argumentet, er dette ikke klart fra funksjonens type, navn eller signatur. Som vi så i forrige artikkel, er det enkelt å påkalle funksjonen.kraft (2, 3)For å unngå forvirring kan vi gi parametere til en funksjon eksterne navn. Vi kan da bruke disse eksterne navnene når funksjonen kalles for entydig å indikere hvilket argument som tilsvarer hvilken parameter. Ta en titt på det oppdaterte eksemplet nedenfor.
func power (base a: Int, eksponent b: Int) -> Int var resultat = a for _ i 1 ...Merk at funksjonens kropp ikke har endret seg siden de lokale navnene ikke er endret. Men når vi påberoper den oppdaterte funksjonen, er forskjellen klar og resultatet er mindre forvirrende.
kraft (base: 2, eksponent: 3)Mens typene av begge funksjonene er identiske,
(Int, Int) -> Int
, funksjonene er forskjellige. Med andre ord er den andre funksjonen ikke en redeklarasjon av den første funksjonen. Syntaxen for å påkalle den andre funksjonen kan minne deg om Objective-C. Ikke bare er argumentene tydelig beskrevet, men kombinasjonen av funksjon og parameternavn beskriver også formålet med funksjonen.I noen tilfeller vil du bruke samme navn for det lokale og det eksterne parameternavnet. Dette er mulig, og det er ikke nødvendig å skrive parameternavnet to ganger. I det følgende eksemplet bruker vi
utgangspunkt
ogeksponent
som de lokale og eksterne parameternavnene.func power (base: Int, eksponent: Int) -> Int var resultat = base for _ i 1 ...Ved å definere ett navn for hver parameter, tjener parameternavnet som det lokale og eksterne navnet på parameteren. Dette betyr også at vi må oppdatere kroppens funksjon.
Det er viktig å merke seg at ved å gi et eksternt navn til en parameter, er du pålagt å bruke det navnet når du påkaller funksjonen. Dette bringer oss til standardverdier.
Standardverdier
Vi dekket standardparameterverdier i forrige artikkel. Dette er funksjonen vi definerte i den artikkelen.
func printDate (dato: Dato, format: String = "YY / MM / dd") -> String let dateFormatter = DateFormatter () dateFormatter.dateFormat = format returdatoFormatter.string (fra: dato)Hva skjer hvis vi ikke definerer et eksternt parameternavn for den andre parameteren, som har en standardverdi?
func printDate (dato: Dato, _ format: String = "YY / MM / dd") -> String let dateFormatter = DateFormatter () dateFormatter.dateFormat = formater returdatoFormatter.string (fra: dato)Kompilatoren ser ikke ut til å bry seg. Men er dette det vi vil ha? Det er best å definere et eksternt parameternavn til valgfrie parametere (parametere med en standardverdi) for å unngå forvirring og tvetydighet.
Legg merke til at vi gjentar oss igjen i det forrige eksempelet. Det er ikke nødvendig å definere et eksternt parameternavn for
Dato
parameter. Det neste eksemplet viser hvaprintDate (_: format :)
Funksjonen ville se ut hvis vi fulgte retningslinjene for Swift API.func printDate (_ date: Date, format: String = "YY / MM / dd") -> String let dateFormatter = DateFormatter () dateFormatter.dateFormat = format return dateFormatter.string (fra: date)Vi kan nå påberope seg
formatDate (_: format :)
funksjon uten å brukeDato
etikett for den første parameteren og med et valgfritt datoformat.printDate (Date ()) printDate (Date (), format: "dd / MM / YY")2. Parametre og mutability
La oss revidere det første eksemplet på denne opplæringen,
printMessage (_ :)
funksjon. Hva skjer hvis vi endrer verdien avbudskap
parameter inne i funksjonens kropp?func printMessage (_ message: String) message = "Skriv ut: \ (melding)" skriv ut (melding)Det tar ikke lang tid for kompilatoren å begynne å klage.
Parametrene til en funksjon er konstanter. Med andre ord, mens vi får tilgang til verdiene av funksjonsparametere, kan vi ikke endre verdien. For å omgå denne begrensningen, erklærer vi en variabel i funksjonens kropp og bruker den variabelen i stedet.
func printMessage (_ message: String) var melding = message message = "Skriv ut: \ (melding)" print (melding)3. Variadiske parametere
Mens begrepet kan høres ulikt i begynnelsen, er variadiske parametre vanlige i programmeringen. En variadisk parameter er en parameter som aksepterer null eller flere verdier. Verdiene må være av samme type. Bruke variadiske parametre i Swift er trivielt, som det følgende eksemplet illustrerer.
func sum (_ args: Int ...) -> Int var resultat = 0 for en i args result + = a returresultat sum (1, 2, 3, 4)Syntaxen er lett å forstå. For å markere en parameter som variadic, legger du til tre punkter til parameterens type. I funksjonen kroppen er variadic parameter tilgjengelig som en matrise. I eksemplet ovenfor,
args
er en rekke avint
verdier.Fordi Swift trenger å vite hvilke argumenter som samsvarer med hvilke parametere, er det nødvendig med en variadisk parameter som den siste parameteren. Det innebærer også at en funksjon kan ha minst en variadisk parameter.
Ovenstående gjelder også hvis en funksjon har parametere med standardverdier. Den variadiske parameteren skal alltid være den siste parameteren.
4. In-Out Parametre
Tidligere i denne opplæringen lærte du at parametrene til en funksjon er konstanter. Hvis du vil overføre en verdi til en funksjon, endre den i funksjonen, og send den tilbake ut av funksjonen, er utgangsparametre det du trenger.
Følgende eksempel viser et eksempel på hvordan utgående parametere fungerer i Swift og hva syntaksen ser ut.
func prefixString (_ streng: inout String, med prefiks: String) string = prefix + stringVi definerer den første parameteren som en in-out parameter ved å legge til
inout
søkeord. Den andre parameteren er en vanlig parameter med et eksternt navn påwithString
og et lokalt navn påprefiks
. Hvordan bruker vi denne funksjonen?var input = "world!" prefixString (& input, med: "Hei,")Vi erklærer en variabel,
inngang
, av typestring
og send det tilprefixString (_: med :)
funksjon. Den andre parameteren er en streng bokstavelig. Ved å påkalle funksjonen, verdien avinngang
variabel blirHei Verden!
. Merk at det første argumentet er prefiks med en ampersand,&
, for å indikere at det er en in-out parameter.Det står selvsagt at konstanter og bokstaver ikke kan sendes inn som in-out parametere. Kompilatoren kaster en feil når du gjør det som illustrert i de følgende eksemplene.
Det er tydelig at in-out parametere ikke kan ha standardverdier eller være variadiske. Hvis du glemmer disse detaljene, påminner kompilatoren deg med en feil.
5. Nesting
I C og Objective-C kan funksjoner og metoder ikke nestes. I Swift er imidlertid nestede funksjoner ganske vanlige. Funksjonene vi så i denne og forrige artikkel er eksempler på globale funksjoner - de er definert i det globale omfanget.
Når vi definerer en funksjon i en global funksjon, refererer vi til den funksjonen som en nestet funksjon. En nestet funksjon har tilgang til verdiene som er definert i sin omsluttende funksjon. Ta en titt på følgende eksempel for å bedre forstå dette.
func printMessage (_ message: String) la a = "hallo verden" func printHelloWorld () print (a)Mens funksjonene i dette eksemplet ikke er veldig nyttig, illustrerer de ideen om nestede funksjoner og fanger verdier. De
printHelloWorld ()
funksjonen er bare tilgjengelig fra innsiden avprintMessage (_ :)
funksjon.Som illustrert i eksemplet, er
printHelloWorld ()
funksjonen har tilgang til konstantenen
. Verdien er fanget av den nestede funksjonen og er derfor tilgjengelig fra den funksjonen. Swift tar seg av å fange verdier, inkludert styring av minnet av disse verdiene.6. Funksjonstyper
Funksjoner som parametere
I forrige artikkel berørte vi kort funksjonstypene. En funksjon har en bestemt type, som består av funksjonens parametertyper og dens returtype. De
printMessage (_ :)
funksjonen er for eksempel av typen(String) -> ()
. Husk at()
symbolisererTomrom
, som tilsvarer en tom tuple.Fordi hver funksjon har en type, er det mulig å definere en funksjon som aksepterer en annen funksjon som en parameter. Følgende eksempel viser hvordan dette virker.
func printMessage (_ message: String) print (message) func printMessage (_ melding: String, med funksjon: (String) -> ()) funksjon (melding) la myMessage = "Hei, verden!" printMessage (minMessage, med: printMessage)De
printMessage (_: med :)
funksjonen aksepterer en streng som sin første parameter og en funksjon av typen(String) -> ()
som sin andre parameter. I funksjonens kropp er funksjonen som vi sender inn, påkalt medbudskap
argument.Eksemplet illustrerer også hvordan vi kan påberope seg
printMessage (_: med :)
funksjon. DemyMessage
Konstant er sendt inn som det første argumentet ogprintMessage (_ :)
fungere som det andre argumentet. Hvor kult er det?Funksjoner som returtyper
Det er også mulig å returnere en funksjon fra en funksjon. Det neste eksemplet er litt opptatt, men det illustrerer hva syntaksen ser ut.
func beregne (_ tillegg: Bool) -> (Int, Int) -> Int func add (_ a: Int, _b: Int) -> Int return a + b func subtrahere (_ a: Int, _ b: Int) -> Int return a - b hvis tillegg return add ellers return subtract la computeFunction = compute (true) la resultatet = computeFunction (1, 2) printDe
beregn (_ :)
funksjonen aksepterer en boolsk og returnerer en funksjon av typen(Int, Int) -> Int
. Deberegn (_ :)
funksjonen inneholder to nestede funksjoner som også er av typen(Int, Int) -> Int
,Legg til(_:_:)
ogtrekke fra(_:_:)
.De
beregn (_ :)
funksjonen returnerer en referanse til entenLegg til(_:_:)
ellertrekke fra(_:_:)
funksjon, basert på verdien avaddisjon
parameter.Eksemplet viser også hvordan du bruker
beregn (_ :)
funksjon. Vi lagrer en referanse til funksjonen som returneres avberegn (_ :)
fungere icomputeFunction
konstant. Vi kaller deretter funksjonen som er lagret icomputeFunction
, passerer inn1
og2
, lagre resultatet iresultat
konstant, og skriv ut verdien avresultat
i standardutgangen. Eksemplet kan se komplisert ut, men det er faktisk lett å forstå hvis du vet hva som skjer.Konklusjon
Du bør nå ha en god forståelse av hvordan funksjoner fungerer i Swift og hva du kan gjøre med dem. Funksjoner er grunnleggende for Swift-språket, og du vil bruke dem mye når du arbeider med Swift.
I den neste artikkelen dykker vi hodet først til lukninger - en kraftig konstruksjon som minner om blokker i C og Objective-C, lukninger i JavaScript og lambdas i Ruby.
Hvis du vil lære hvordan du bruker Swift 3 til å kode sanntidsprogrammer, sjekk ut kurset ditt Opprett IOS Apps With Swift 3. Enten du er ny i iOS app-utvikling eller ønsker å skifte bryteren fra Objective-C, dette kurset vil komme i gang med Swift for app utvikling.