Swift From Scratch Funktionsparametrar, Typer och Nesting

I den föregående artikeln undersökte vi grunderna för funktioner i Swift. Funktionerna har dock mycket mer att erbjuda. I den här artikeln fortsätter vi vår utforskning av funktioner och tittar på funktionsparametrar, nestning och typer.

1. Lokala och externa parameternamn

Parameternamn

Låt oss återfå ett av exemplen från föregående artikel. De printMessage (meddelande :) funktionen definierar en parameter, meddelande.

func printMessage (meddelande: String) skriv ut (meddelande)

Vi tilldelar ett namn, meddelande, till parametern och använd det här namnet när vi ringer till funktionen.

printMessage (meddelande: "Hej världen!")

Men märk att vi också använder samma namn för att referera till parametervärdet i funktionens kropp.

func printMessage (meddelande: String) skriv ut (meddelande)

I Swift har en parameter alltid a lokal parameternamn, och det har eventuellt en extern parameternamn. I exemplet är de lokala och externa parameterns namn identiska.

API-riktlinjer

Från Swift 3 har Swift-teamet definierat en tydlig uppsättning API-riktlinjer. Jag kommer inte att gå in i dessa riktlinjer i denna handledning, men jag vill påpeka att definitionen av printMessage (meddelande :) funktionen avviker från dessa riktlinjer. Namnet på funktionen innehåller ordet meddelande, och parametern heter också meddelande. Med andra ord, vi upprepar oss själva.

Det skulle vara mer elegant om vi kunde åberopa printMessage (meddelande :) funktion utan meddelande nyckelord. Detta är vad jag har i åtanke.

printMessage ("Hej världen!")

Detta är möjligt, och det överensstämmer med Swift API-riktlinjerna. Men vad är annorlunda? Skillnaden är lätt att få plats om vi tittar på den uppdaterade funktionsdefinitionen. Det uppdaterade exemplet avslöjar också mer om funktionalitetens anatomi i Swift.

func printMessage (_ message: String) skriv ut (meddelande)

I en funktionsdefinition definieras varje parameter av ett externt parameternamn, ett lokalt parameternamn, ett kolon och typen av parametern. Om de lokala och externa parameterns namn är identiska skriver vi bara parameternamnet en gång. Därför definierar det första exemplet ett parameternamn, meddelande.

Om vi ​​inte vill tilldela ett externt parameternamn till en parameter använder vi _, en understrykning. Detta informerar kompilatorn om att parametern inte har ett externt parameternamn, och det betyder att vi kan utelämna parameterns namn när funktionen påkallas.

Externa parameternamn

Objektiv-C är känd för sina långa metodenamn. Även om det här kan se utklassigt och inelegant mot utomstående, gör det metoderna lätt att förstå och, om de väljs väl, mycket beskrivande. Swift-teamet förstod denna fördel och introducerade externa parameternamn från dag ett.

När en funktion accepterar flera parametrar är det inte alltid uppenbart vilket argument som motsvarar vilken parameter. Ta en titt på följande exempel för att bättre förstå problemet. Observera att parametrarna inte har ett externt parameternamn.

func power (_a: Int, _b: Int) -> Int var resultat = a för _ i 1 ... 

De kraft(_:_:) funktionen höjer värdet av en av exponenten b. Båda parametrarna är av typ int. Medan de flesta människor intuitivt kommer att överföra basvärdet som det första argumentet och exponenten som det andra argumentet, är detta inte klart från funktionens typ, namn eller signatur. Som vi såg i den föregående artikeln är det enkelt att påkalla funktionen.

kraft (2, 3)

För att undvika förvirring kan vi ge parametrarna för en funktion externa namn. Vi kan då använda dessa externa namn när funktionen kallas för att entydigt ange vilket argument som motsvarar vilken parameter. Ta en titt på det uppdaterade exemplet nedan.

func power (bas a: Int, exponent b: Int) -> Int var resultat = a för _ i 1 ... 

Observera att funktionens kropp inte har ändrats eftersom de lokala namnen inte har ändrats. Men när vi anropar den uppdaterade funktionen är skillnaden klar och resultatet är mindre förvirrande.

kraft (bas: 2, exponent: 3)

Medan typerna av båda funktionerna är identiska, (Int, Int) -> Int, funktionerna är olika. Med andra ord är den andra funktionen inte en redeklaration av den första funktionen. Syntaxen för att åberopa den andra funktionen kan påminna dig om Objective-C. Argumenten beskrivs inte bara tydligt, men kombinationen av funktions- och parameternamn beskriver också syftet med funktionen.

I vissa fall vill du använda samma namn för det lokala och det externa parameterns namn. Detta är möjligt, och det är inte nödvändigt att skriva parameterns namn två gånger. I följande exempel använder vi bas och exponent som de lokala och externa parametrarna.

func power (bas: Int, exponent: Int) -> Int var resultat = bas för _ i 1 ... 

Genom att definiera ett namn för varje parameter används parameternamnet som det lokala och externa namnet på parametern. Detta innebär också att vi behöver uppdatera funktionens kropp.

Det är viktigt att notera att genom att ange ett externt namn för en parameter måste du använda det namnet när du aktiverar funktionen. Detta ger oss standardvärden.

Ursprungliga värden

Vi täckte standardparametervärden i föregående artikel. Det här är den funktion som vi definierade i den artikeln.

func printDate (datum: Datum, format: String = "YY / MM / dd") -> String let dateFormatter = DateFormatter () dateFormatter.dateFormat = format returneringsdatumFormatter.string (från: datum)

Vad händer om vi inte definierar ett externt parameternamn för den andra parametern, som har ett standardvärde?

func printDate (datum: Datum, _format: String = "YY / MM / dd") -> String let dateFormatter = DateFormatter () dateFormatter.dateFormat = format returneringsdatumFormatter.string (från: datum)

Samlaren verkar inte bryr sig om. Men är det vad vi vill ha? Det är bäst att definiera ett externt parameternamn till valfria parametrar (parametrar med ett standardvärde) för att undvika förvirring och tvetydighet.

Observera att vi upprepar oss själva igen i föregående exempel. Det finns inget behov av att definiera ett externt parameternamn för datum parameter. Nästa exempel visar vad printDate (_: format :) funktionen skulle se ut om vi följde Swift API-riktlinjerna.

func printDate (_ datum: Datum, format: String = "YY / MM / dd") -> String let dateFormatter = DateFormatter () dateFormatter.dateFormat = format returneringsdatumFormatter.string (från: datum)

Vi kan nu åberopa formatDate (_: format :) funktion utan att använda datum etikett för den första parametern och med ett valfritt datumformat.

printDate (Date ()) printDate (Datum (), format: "dd / MM / YY")

2. Parametrar och ömsesidighet

Låt oss återgå till det första exemplet på denna handledning, printMessage (_ :) fungera. Vad händer om vi ändrar värdet av meddelande parametern inuti funktionens kropp?

func printMessage (_ message: String) message = "Skriv ut: \ (meddelande)" skriv ut (meddelande)

Det tar inte lång tid för kompilatorn att börja klaga.

Parametrarna för en funktion är konstanter. Med andra ord, medan vi kan komma åt värdena på funktionsparametrar kan vi inte ändra sitt värde. För att omarbeta denna begränsning, deklarerar vi en variabel i funktionens kropp och använder den variabeln istället.

func printMessage (_ message: String) var message = message message = "Skriv ut: \ (meddelande)" skriv ut (meddelande)

3. Variadiska parametrar

Medan termen kanske låter udda i början är variadiska parametrar vanliga vid programmering. En variadisk parameter är en parameter som accepterar noll eller flera värden. Värdena måste vara av samma typ. Att använda variadiska parametrar i Swift är trivial, som följande exempel illustrerar.

func sum (_ args: Int ...) -> Int var resultat = 0 för en i args result + = a returresultat summa (1, 2, 3, 4)

Syntaxen är lätt att förstå. För att markera en parameter som variadic, lägger du till tre punkter till parameterns typ. I funktionskroppen är variadparametern tillgänglig som en grupp. I ovanstående exempel, args är en uppsättning av int värden.

Eftersom Swift behöver veta vilka argument som motsvarar vilka parametrar krävs en variadisk parameter för att vara den sista parametern. Det innebär också att en funktion kan ha högst en variad parameter.

Ovanstående gäller även om en funktion har parametrar med standardvärden. Den variadiska parametern ska alltid vara den sista parametern.

4. In-Out Parametrar

Tidigare i denna handledning lärde du dig att parametrarna för en funktion är konstanter. Om du vill skicka ett värde till en funktion, ändra det i funktionen och skicka det tillbaka ur funktionen, är in-out-parametrar det du behöver.

Följande exempel visar ett exempel på hur utgående parametrar fungerar i Swift och vad syntaxen ser ut.

func prefixString (_ string: inout String, med prefix: String) string = prefix + string

Vi definierar den första parametern som en in-out parameter genom att lägga till in ut nyckelord. Den andra parametern är en vanlig parameter med ett externt namn på withString och ett lokalt namn på prefix. Hur åberopar vi den här funktionen?

var input = "world!" prefixString (& input, med: "Hej")

Vi förklarar en variabel, inmatning, av typ Sträng och skicka den till prefixString (_: med :) fungera. Den andra parametern är en sträng bokstavlig. Genom att aktivera funktionen, värdet av inmatning variabel blir Hej världen!. Observera att det första argumentet är prefixat med en ampersand, &, för att ange att det är en in-out-parameter.

Det står självklart att konstanter och bokstäver inte kan överföras som in-out parametrar. Kompilatorn kastar ett fel när du gör det som illustreras i följande exempel.

Det är uppenbart att in-out parametrar inte kan ha standardvärden eller vara variadiska. Om du glömmer bort dessa uppgifter påminner kompilatorn dig om ett fel.

5. Nesting

I C och Objective-C kan funktioner och metoder inte nästas. I Swift är dock kapslade funktioner ganska vanliga. Funktionerna som vi såg i denna och föregående artikel är exempel på globala funktioner, de definieras i det globala räckviddet.

När vi definierar en funktion i en global funktion hänvisar vi till den funktionen som en kapslad funktion. En kapslad funktion har tillgång till de värden som definieras i dess omslutningsfunktion. Ta en titt på följande exempel för att bättre förstå detta.

func printMessage (_ message: String) let a = "hej världen" func printHelloWorld () print (a)

Medan funktionerna i det här exemplet inte är väldigt användbara illustrerar de idén om kapslade funktioner och fånga värden. De printHelloWorld () funktionen är endast tillgänglig från inom printMessage (_ :) fungera.

Såsom illustreras i exemplet, printHelloWorld () funktionen har tillgång till konstanten en. Värdet fångas av den kapslade funktionen och är därför tillgänglig från den funktionen. Swift tar hand om att fånga värden, inklusive hantering av minnet av dessa värden.

6. Funktionstyper

Funktioner som parametrar

I föregående artikel berörde vi kortfattat funktionstyperna. En funktion har en viss typ, som består av funktionens parametertyper och dess returtyp. De printMessage (_ :) funktionen är till exempel av typen (String) -> (). Kom ihåg det () symboliserar Ogiltig, vilket motsvarar en tom tupel.

Eftersom varje funktion har en typ är det möjligt att definiera en funktion som accepterar en annan funktion som en parameter. Följande exempel visar hur det fungerar.

func printMessage (_ message: String) skriv ut (meddelande) func printMessage (_ message: String, med funktion: (String) -> ()) funktion (meddelande) låt myMessage = "Hej världen!" printMessage (myMessage, med: printMessage)

De printMessage (_: med :) funktionen accepterar en sträng som sin första parameter och en funktion av typen (String) -> () som sin andra parameter. I funktionens kropp är den funktion som vi passerar in påkallad med meddelande argument.

Exemplet illustrerar också hur vi kan åberopa printMessage (_: med :) fungera. De mitt meddelande Konstant passeras som första argumentet och printMessage (_ :) fungera som det andra argumentet. Hur coolt är inte det?

Funktioner som Retardtyper

Det är också möjligt att returnera en funktion från en funktion. Nästa exempel är lite konstruerat, men det illustrerar vad syntaxen ser ut.

func compute (_ addition: Bool) -> (Int, Int) -> Int func add (_a: Int, _b: Int) -> Int return a + b func subtrahera (_a: Int, _ b: Int) -> Int return a - b om addition return add annars return subtract låt computeFunction = compute (true) let result = computeFunction (1, 2)

De beräkna(_:) funktionen accepterar en booleska och returnerar en funktion av typen (Int, Int) -> Int. De beräkna(_:) funktionen innehåller två kapslade funktioner som också är av typen (Int, Int) -> Int, Lägg till(_:_:) och subtrahera(_:_:).

De beräkna(_:) funktionen returnerar en referens till antingen Lägg till(_:_:) eller den subtrahera(_:_:) funktion, baserat på värdet av tillägg parameter.

Exemplet visar också hur man använder beräkna(_:) fungera. Vi lagrar en referens till den funktion som returneras av beräkna(_:) funktion i compute konstant. Vi anropar sedan funktionen som lagrats i compute, passerar in 1 och 2, lagra resultatet i resultat konstant och skriv ut värdet på resultat i standardutgången. Exemplet kan se komplex ut, men det är faktiskt lätt att förstå om du vet vad som händer.

Slutsats

Du borde nu ha en god förståelse för hur funktioner fungerar i Swift och vad du kan göra med dem. Funktioner är grundläggande för Swift-språk, och du kommer att använda dem i stor utsträckning när du arbetar med Swift.

I nästa artikel dykar vi huvudet först i stängningar-en kraftfull konstruktion som påminner om block i C och Objective-C, stängningar i JavaScript och lambdas i Ruby.

Om du vill lära dig hur du använder Swift 3 för att koda i verkliga applikationer, kolla in vår kurs Skapa iOS Apps With Swift 3. Om du är ny i iOS-apputveckling eller letar efter att göra omkopplaren från Objective-C, så här Kursen kommer att komma igång med Swift för apputveckling.