Swift from Scratch Functieparameters, typen en nesten

In het vorige artikel hebben we de basis van functies in Swift onderzocht. Functies hebben echter nog veel meer te bieden. In dit artikel gaan we verder met het verkennen van functies en kijken we naar functieparameters, nesten en typen.

1. Lokale en externe parameternamen

Parameternamen

Laten we een van de voorbeelden uit het vorige artikel opnieuw bekijken. De printMessage (message :) functie definieert één parameter, bericht.

func printMessage (bericht: String) print (bericht)

We wijzen een naam toe, bericht, naar de parameter en gebruik deze naam wanneer we de functie aanroepen.

printMessage (bericht: "Hallo wereld!")

Maar merk op dat we ook dezelfde naam gebruiken om te verwijzen naar de waarde van de parameter in de hoofdtekst van de functie.

func printMessage (bericht: String) print (bericht)

In Swift heeft een parameter altijd een lokaal parameternaam, en deze heeft optioneel een extern parameternaam. In het voorbeeld zijn de lokale en externe parameternamen identiek.

API-richtlijnen

Vanaf Swift 3 heeft het Swift-team een ​​duidelijke set API-richtlijnen gedefinieerd. Ik zal in deze tutorial niet ingaan op die richtlijnen, maar ik wil erop wijzen dat de definitie van de printMessage (message :) functie wijkt af van die richtlijnen. De naam van de functie bevat het woord bericht, en de parameter heeft ook een naam bericht. Met andere woorden, we herhalen onszelf.

Het zou eleganter zijn als we de printMessage (message :) functioneren zonder de bericht trefwoord. Dit is wat ik in gedachten heb.

printMessage ("Hallo, wereld!")

Dit is mogelijk en het is meer in lijn met de Swift API-richtlijnen. Maar wat is anders? Het verschil is gemakkelijk te herkennen als we de bijgewerkte functiedefinitie bekijken. Het bijgewerkte voorbeeld onthult ook meer informatie over de anatomie van functies in Swift.

func printMessage (_ message: String) print (bericht)

In een functiedefinitie wordt elke parameter gedefinieerd door een externe parameternaam, een lokale parameternaam, een dubbele punt en het type parameter. Als de lokale en externe parameternamen identiek zijn, schrijven we de parameternaam slechts eenmaal. Daarom definieert het eerste voorbeeld één parameternaam, bericht.

Als we geen externe parameternaam aan een parameter willen toewijzen, gebruiken we de _, een onderstrepingsteken. Dit informeert de compiler dat de parameter geen externe parameternaam heeft, en dat betekent dat we de parameternaam kunnen weglaten wanneer de functie wordt aangeroepen.

Externe parameternamen

Objective-C staat bekend om zijn lange methode namen. Hoewel dit misschien onhandig en onelegant lijkt voor buitenstaanders, maakt het methoden gemakkelijk te begrijpen en, indien goed gekozen, zeer beschrijvend. Het Swift-team begreep dit voordeel en introduceerde vanaf de eerste dag externe parameternamen.

Wanneer een functie verschillende parameters accepteert, is het niet altijd duidelijk welk argument overeenkomt met welke parameter. Bekijk het volgende voorbeeld om het probleem beter te begrijpen. Merk op dat de parameters geen externe parameternaam hebben.

func power (_ a: Int, _ b: Int) -> Int var result = a for _ in 1 ... 

De vermogen (_: _ :) functie verhoogt de waarde van een door de exponent b. Beide parameters zijn van het type Int. Hoewel de meeste mensen intuïtief de basiswaarde doorgeven als het eerste argument en de exponent als het tweede argument, is dit niet duidelijk uit het type, de naam of de handtekening van de functie. Zoals we in het vorige artikel zagen, is het aanroepen van de functie eenvoudig.

vermogen (2, 3)

Om verwarring te voorkomen, kunnen we de parameters van een functie externe namen geven. We kunnen dan deze externe namen gebruiken als de functie wordt aangeroepen om ondubbelzinnig aan te geven welk argument overeenkomt met welke parameter. Bekijk het bijgewerkte voorbeeld hieronder.

func power (base a: Int, exponent b: Int) -> Int var result = a for _ in 1 ... 

Merk op dat de body van de functie niet is gewijzigd sinds de lokale namen niet zijn gewijzigd. Wanneer we echter de bijgewerkte functie aanroepen, is het verschil duidelijk en het resultaat is minder verwarrend.

vermogen (basis: 2, exponent: 3)

Hoewel de typen van beide functies identiek zijn, (Int, Int) -> Int, de functies zijn anders. Met andere woorden, de tweede functie is geen herclaratie van de eerste functie. De syntaxis om de tweede functie aan te roepen, kan u herinneren aan Objective-C. Niet alleen zijn de argumenten duidelijk beschreven, maar de combinatie van functie- en parameternamen beschrijft ook het doel van de functie.

In sommige gevallen wilt u dezelfde naam gebruiken voor de lokale en de externe parameternaam. Dit is mogelijk en het is niet nodig om de parameternaam tweemaal in te voeren. In het volgende voorbeeld gebruiken we baseren en exponent als de lokale en externe parameternamen.

func power (basis: Int, exponent: Int) -> Int var result = base for _ in 1 ... 

Door voor elke parameter één naam te definiëren, dient de parameternaam als de lokale en externe naam van de parameter. Dit betekent ook dat we de hoofdtekst van de functie moeten bijwerken.

Het is belangrijk om op te merken dat door een externe naam voor een parameter op te geven, u verplicht bent om die naam te gebruiken bij het oproepen van de functie. Dit brengt ons naar standaardwaarden.

Standaard waarden

We hebben standaard parameterwaarden in het vorige artikel besproken. Dit is de functie die we in dat artikel hebben gedefinieerd.

func printDate (datum: Datum, formaat: String = "YY / MM / dd") -> String let dateFormatter = DateFormatter () dateFormatter.dateFormat = format return dateFormatter.string (from: date)

Wat gebeurt er als we geen externe parameternaam definiëren voor de tweede parameter, die een standaardwaarde heeft?

func printDate (datum: Date, _ format: String = "YY / MM / dd") -> String let dateFormatter = DateFormatter () dateFormatter.dateFormat = format return dateFormatter.string (from: date)

Het lijkt de compiler niet uit te maken. Maar is dit wat we willen? Het is het beste om een ​​externe parameternaam te definiëren voor optionele parameters (parameters met een standaardwaarde) om verwarring en dubbelzinnigheid te voorkomen.

Merk op dat we onszelf opnieuw herhalen in het vorige voorbeeld. Het is niet nodig om een ​​externe parameternaam te definiëren voor de datum parameter. Het volgende voorbeeld laat zien wat de PrintDate (_: format :) functie zou eruit zien als we de Swift API-richtlijnen zouden volgen.

func printDate (_ date: Date, format: String = "YY / MM / dd") -> String let dateFormatter = DateFormatter () dateFormatter.dateFormat = format return dateFormatter.string (from: date)

We kunnen nu het formatDate (_: format :) functie zonder de datum label voor de eerste parameter en met een optioneel datumformaat.

printDate (Datum ()) printDate (Datum (), formaat: "dd / MM / YY")

2. Parameters en veranderlijkheid

Laten we het eerste voorbeeld van deze tutorial opnieuw bekijken, de printMessage (_ :) functie. Wat gebeurt er als we de waarde van de bericht parameter in de body van de functie?

func printMessage (_ message: String) message = "Afdrukken: \ (bericht)" afdrukken (bericht)

Het duurt niet lang voordat de compiler begint te klagen.

De parameters van een functie zijn constanten. Met andere woorden, hoewel we toegang hebben tot de waarden van functieparameters, kunnen we hun waarde niet veranderen. Om deze beperking te omzeilen, declareren we een variabele in het lichaam van de functie en gebruiken die variabele in plaats daarvan.

func printMessage (_ message: String) var message = message message = "Afdrukken: \ (bericht)" afdrukken (bericht)

3. Variadische parameters

Hoewel de term in het begin misschien vreemd klinkt, zijn variadische parameters gebruikelijk bij het programmeren. Een variadische parameter is een parameter die nul of meer waarden accepteert. De waarden moeten van hetzelfde type zijn. Het gebruik van variadische parameters in Swift is triviaal, zoals het volgende voorbeeld illustreert.

func sum (_ args: Int ...) -> Int var result = 0 for a in args result + = a return result sum (1, 2, 3, 4)

De syntaxis is gemakkelijk te begrijpen. Als u een parameter als variabel wilt markeren, voegt u drie punten toe aan het type van de parameter. In de hoofdtekst van de functie is de variadische parameter toegankelijk als een array. In het bovenstaande voorbeeld, args is een array van Int waarden.

Omdat Swift moet weten welke argumenten overeenkomen met welke parameters, is een variadische parameter vereist om de laatste parameter te zijn. Het impliceert ook dat een functie maximaal één variadische parameter kan hebben.

Het bovenstaande is ook van toepassing als een functie parameters heeft met standaardwaarden. De variadische parameter moet altijd de laatste parameter zijn.

4. In-outparameters

Eerder in deze tutorial leerde je dat de parameters van een functie constanten zijn. Als u een waarde wilt doorgeven aan een functie, deze in de functie wilt wijzigen en deze uit de functie wilt overbrengen, zijn in-out parameters wat u nodig hebt.

In het volgende voorbeeld ziet u een voorbeeld van hoe in-out-parameters in Swift werken en hoe de syntaxis eruit ziet.

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

We definiëren de eerste parameter als een in-uit parameter door het toevoegen van de in uit trefwoord. De tweede parameter is een normale parameter met een externe naam van withstring en een lokale naam van voorvoegsel. Hoe roepen we deze functie aan??

var input = "world!" prefixString (& input, met: "Hallo,")

We verklaren een variabele, invoer, van type Draad en geef het door aan de prefixString (_: met :) functie. De tweede parameter is een letterlijke tekenreeks. Door de functie aan te roepen, de waarde van de invoer variabele wordt Hallo Wereld!. Merk op dat het eerste argument voorafgegaan wordt door een ampersand, &, om aan te geven dat het een in-uit parameter is.

Het spreekt voor zich dat constanten en letterlijke waarden niet kunnen worden doorgegeven als in-out parameters. De compiler genereert een foutmelding wanneer u dit doet, zoals in de volgende voorbeelden wordt geïllustreerd.

Het is duidelijk dat in-out parameters geen standaardwaarden kunnen hebben of variadisch zijn. Als u deze details vergeet, herinnert de compiler u vriendelijk aan een fout.

5. Nesten

In C en Objective-C kunnen functies en methoden niet worden genest. In Swift zijn geneste functies echter vrij gebruikelijk. De functies die we in dit en het vorige artikel hebben gezien, zijn voorbeelden van algemene functies: ze zijn gedefinieerd in de globale reikwijdte.

Wanneer we een functie definiëren binnen een globale functie, verwijzen we naar die functie als een geneste functie. Een geneste functie heeft toegang tot de waarden die zijn gedefinieerd in de bijbehorende functie. Bekijk het volgende voorbeeld om dit beter te begrijpen.

func printMessage (_ message: String) let a = "hallo wereld" func printHelloWorld () print (a)

Hoewel de functies in dit voorbeeld niet erg nuttig zijn, illustreren ze het idee van geneste functies en het vastleggen van waarden. De printHelloWorld () functie is alleen toegankelijk vanuit de printMessage (_ :) functie.

Zoals geïllustreerd in het voorbeeld, de printHelloWorld () functie heeft toegang tot de constante een. De waarde wordt vastgelegd door de geneste functie en is daarom toegankelijk vanuit die functie. Swift zorgt voor het vastleggen van waarden, inclusief het beheren van het geheugen van die waarden.

6. Functietypen

Functies als parameters

In het vorige artikel hebben we kort ingegaan op functietypen. Een functie heeft een bepaald type, samengesteld uit de parametertypen van de functie en het retourneringstype. De printMessage (_ :) de functie is bijvoorbeeld van het type (String) -> (). Onthoudt dat () symboliseert leegte, wat overeenkomt met een lege tuple.

Omdat elke functie een type heeft, is het mogelijk om een ​​functie te definiëren die een andere functie als parameter accepteert. Het volgende voorbeeld laat zien hoe dit werkt.

func printMessage (_ message: String) print (bericht) func printMessage (_ message: String, with function: (String) -> ()) function (message) let myMessage = "Hallo, wereld!" printMessage (mijnMessage, met: printMessage)

De printMessage (_: met :) functie accepteert een string als zijn eerste parameter en een functie van het type (String) -> () als zijn tweede parameter. In het lichaam van de functie wordt de functie die we doorgeven opgeroepen met de bericht argument.

Het voorbeeld illustreert ook hoe we het kunnen oproepen printMessage (_: met :) functie. De mijn bericht constante wordt doorgegeven als het eerste argument en de printMessage (_ :) fungeren als het tweede argument. Hoe cool is dat?

Functies als retourtypen

Het is ook mogelijk om een ​​functie uit een functie te retourneren. Het volgende voorbeeld is een beetje gekunsteld, maar het illustreert hoe de syntaxis eruit ziet.

func compute (_ toevoeging: Bool) -> (Int, Int) -> Int func add (_ a: Int, _ b: Int) -> Int return a + b func subtract (_ a: Int, _ b: Int) -> Int return a - b als optellen retourneren else retourneren aftrekken laat computeFunction = berekenen (true) laat resultaat = computeFunction (1, 2) afdrukken (resultaat)

De berekenen(_:) function accepteert een boolean en retourneert een functie van het type (Int, Int) -> Int. De berekenen(_:) functie bevat twee geneste functies die ook van het type zijn (Int, Int) -> Int, toevoegen(_:_:) en aftrekken(_:_:).

De berekenen(_:) functie geeft een verwijzing naar de toevoegen(_:_:) of de aftrekken(_:_:) functie, gebaseerd op de waarde van de toevoeging parameter.

Het voorbeeld laat ook zien hoe u de berekenen(_:) functie. We slaan een verwijzing op naar de functie die wordt geretourneerd door de berekenen(_:) functie in de computeFunction constante. Vervolgens roepen we de functie op die is opgeslagen in computeFunction, binnenkomen 1 en 2, sla het resultaat op in de resultaat constant en druk de waarde af van resultaat in de standaarduitvoer. Het voorbeeld kan er ingewikkeld uitzien, maar het is eigenlijk eenvoudig te begrijpen als u weet wat er aan de hand is.

Conclusie

U zou nu een goed begrip moeten hebben van hoe functies in Swift werken en wat u ermee kunt doen. Functies zijn fundamenteel voor de Swift-taal en u zult ze veel gebruiken bij het werken met Swift.

In het volgende artikel duiken we eerst in closures - een krachtige constructie die doet denken aan blokken in C en Objective-C, sluitingen in JavaScript en lambda's in Ruby.

Als je wilt leren hoe je Swift 3 kunt gebruiken om real-world apps te coderen, bekijk dan onze cursus Maak iOS-apps met Swift 3. Of je nu nieuw bent bij de ontwikkeling van iOS-apps of op zoek bent naar de overstap van Objective-C, deze Natuurlijk begin je met Swift voor app-ontwikkeling.