Schnell von Grund auf Funktionsparameter, Typen und Verschachtelung

Im vorigen Artikel haben wir die Grundlagen der Funktionen in Swift untersucht. Funktionen haben jedoch noch viel mehr zu bieten. In diesem Artikel setzen wir unsere Erkundung der Funktionen fort und untersuchen Funktionsparameter, Verschachtelung und Typen.

1. Lokale und externe Parameternamen

Parameternamen

Lassen Sie uns eines der Beispiele aus dem vorherigen Artikel noch einmal besuchen. Das printMessage (Nachricht :) Funktion definiert einen Parameter, Botschaft.

func printMessage (Nachricht: Zeichenfolge) print (Nachricht)

Wir vergeben einen Namen, Botschaft, zu dem Parameter und verwenden Sie diesen Namen, wenn Sie die Funktion aufrufen.

printMessage (Nachricht: "Hallo, Welt!")

Beachten Sie jedoch, dass wir denselben Namen verwenden, um auf den Wert des Parameters im Rumpf der Funktion zu verweisen.

func printMessage (Nachricht: Zeichenfolge) print (Nachricht)

In Swift hat ein Parameter immer eine lokal Parametername und hat optional eine extern Parametername. Im Beispiel sind die lokalen und externen Parameternamen identisch.

API-Richtlinien

Seit Swift 3 hat das Swift-Team klare API-Richtlinien definiert. Ich werde nicht auf diese Richtlinien in diesem Tutorial eingehen, aber ich möchte darauf hinweisen, dass die Definition der printMessage (Nachricht :) Funktion weicht von diesen Richtlinien ab. Der Name der Funktion enthält das Wort Botschaft, und der Parameter wird auch benannt Botschaft. Mit anderen Worten, wir wiederholen uns.

Es wäre eleganter, wenn wir uns anrufen könnten printMessage (Nachricht :) Funktion ohne Botschaft Stichwort. Daran habe ich gedacht.

printMessage ("Hallo, Welt!")

Dies ist möglich und entspricht mehr den Swift-API-Richtlinien. Was ist aber anders? Der Unterschied ist leicht zu erkennen, wenn wir die aktualisierte Funktionsdefinition betrachten. Das aktualisierte Beispiel zeigt auch mehr über die Anatomie der Funktionen in Swift.

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

In einer Funktionsdefinition wird jeder Parameter durch einen externen Parameternamen, einen lokalen Parameternamen, einen Doppelpunkt und den Typ des Parameters definiert. Wenn die lokalen und externen Parameternamen identisch sind, schreiben wir den Parameternamen nur einmal. Deshalb definiert das erste Beispiel einen Parameternamen, Botschaft.

Wenn wir einem Parameter keinen externen Parameternamen zuordnen wollen, verwenden wir den _, ein Unterstrich. Dadurch wird der Compiler informiert, dass der Parameter keinen externen Parameternamen hat. Dies bedeutet, dass wir den Parameternamen beim Aufruf der Funktion weglassen können.

Externe Parameternamen

Objective-C ist bekannt für seine langen Methodennamen. Dies mag für Außenstehende unbeholfen und unelegant erscheinen, aber es macht Methoden verständlich und, wenn sie gut gewählt werden, sehr anschaulich. Das Swift-Team verstand diesen Vorteil und führte vom ersten Tag an externe Parameternamen ein.

Wenn eine Funktion mehrere Parameter akzeptiert, ist nicht immer ersichtlich, welches Argument welchem ​​Parameter entspricht. Sehen Sie sich das folgende Beispiel an, um das Problem besser zu verstehen. Beachten Sie, dass die Parameter keinen externen Parameternamen haben.

Funktionsstärke (_ a: Int, _ b: Int) -> Int var Ergebnis = a für _ in 1… 

Das Leistung(_:_:) Funktion erhöht den Wert von ein vom Exponenten b. Beide Parameter sind vom Typ Int. Während die meisten Benutzer intuitiv den Basiswert als erstes Argument und den Exponenten als zweites Argument übergeben, ist der Typ, der Name oder die Signatur der Funktion nicht klar. Wie wir im vorherigen Artikel gesehen haben, ist das Aufrufen der Funktion unkompliziert.

Leistung (2, 3)

Um Verwirrung zu vermeiden, können wir den Parametern einer Funktion externe Namen geben. Wir können diese externen Namen dann verwenden, wenn die Funktion aufgerufen wird, um eindeutig anzugeben, welches Argument welchem ​​Parameter entspricht. Schauen Sie sich das aktualisierte Beispiel unten an.

Funkleistung (Basis a: Int, Exponent b: Int) -> Int var Ergebnis = a für _ in 1… 

Beachten Sie, dass sich der Rumpf der Funktion nicht geändert hat, da sich die lokalen Namen nicht geändert haben. Wenn wir jedoch die aktualisierte Funktion aufrufen, ist der Unterschied klar und das Ergebnis ist weniger verwirrend.

Leistung (Basis: 2, Exponent: 3)

Während die Typen beider Funktionen identisch sind, (Int, Int) -> Int, Die Funktionen sind unterschiedlich. Mit anderen Worten, die zweite Funktion ist keine Redeklaration der ersten Funktion. Die Syntax zum Aufrufen der zweiten Funktion erinnert Sie möglicherweise an Objective-C. Die Argumente werden nicht nur klar beschrieben, sondern die Kombination von Funktions- und Parameternamen beschreibt auch den Zweck der Funktion.

In einigen Fällen möchten Sie denselben Namen für den lokalen und den externen Parameternamen verwenden. Dies ist möglich und Sie müssen den Parameternamen nicht zweimal eingeben. Im folgenden Beispiel verwenden wir Base und Exponent als lokale und externe Parameternamen.

Funktionsstärke (Basis: Int, Exponent: Int) -> Int var Ergebnis = Basis für _ in 1… 

Durch die Definition eines Namens für jeden Parameter dient der Parametername als lokaler und externer Name des Parameters. Dies bedeutet auch, dass wir den Körper der Funktion aktualisieren müssen.

Beachten Sie, dass Sie durch Angabe eines externen Namens für einen Parameter diesen Namen beim Aufrufen der Funktion verwenden müssen. Dies bringt uns zu Standardwerten.

Standardwerte

Im vorherigen Artikel haben wir die Standardparameterwerte behandelt. Dies ist die Funktion, die wir in diesem Artikel definiert haben.

func printDate (Datum: Datum, Format: String = "JJ / MM / TT") -> String let dateFormatter = DateFormatter () dateFormatter.dateFormat = Format Rückgabe dateFormatter.string (von: Datum)

Was passiert, wenn wir keinen externen Parameternamen für den zweiten Parameter definieren, der einen Standardwert hat?

func printDate (date: Date, _ format: String = "YY / MM / dd") -> String let dateFormatter = DateFormatter () dateFormatter.dateFormat = Format Rückgabe dateFormatter.string (von: Datum)

Der Compiler scheint das nicht zu interessieren. Aber wollen wir das? Es ist am besten, einen externen Parameternamen für optionale Parameter (Parameter mit einem Standardwert) zu definieren, um Verwirrung und Mehrdeutigkeit zu vermeiden.

Beachten Sie, dass wir uns im vorherigen Beispiel noch einmal wiederholen. Es ist nicht erforderlich, einen externen Parameternamen für die zu definieren Datum Parameter. Das nächste Beispiel zeigt was printDate (_: format :) Diese Funktion würde aussehen, wenn wir den Swift-API-Richtlinien folgen würden.

func printDate (_ date: Date, Format: String = "JJ / MM / TT") -> String let dateFormatter = DateFormatter () dateFormatter.dateFormat = Format Rückgabe dateFormatter.string (von: Datum)

Wir können jetzt das aufrufen formatDate (_: format :) Funktion ohne Verwendung der Datum Label für den ersten Parameter und mit einem optionalen Datumsformat.

printDate (Date ()) PrintDate (Date (), Format: "TT / MM / JJ")

2. Parameter und Mutabilität

Lassen Sie uns noch einmal das erste Beispiel dieses Tutorials, das printMessage (_ :) Funktion. Was passiert, wenn wir den Wert von ändern? Botschaft Parameter innerhalb des Funktionskörpers?

func printMessage (_ message: String) message = "Drucken: \ (Nachricht)" print (Nachricht)

Es dauert nicht lange, bis sich der Compiler beschwert.

Die Parameter einer Funktion sind Konstanten. Mit anderen Worten, während wir auf die Werte von Funktionsparametern zugreifen können, können wir deren Wert nicht ändern. Um diese Einschränkung zu umgehen, deklarieren wir eine Variable im Rumpf der Funktion und verwenden stattdessen diese Variable.

func printMessage (_ message: String) var message = message message = "Drucken: \ (Nachricht)" print (Nachricht)

3. Variadische Parameter

Während der Begriff auf den ersten Blick ungerade klingt, sind bei der Programmierung verschiedene Parameter üblich. Ein variadischer Parameter ist ein Parameter, der null oder mehr Werte akzeptiert. Die Werte müssen vom selben Typ sein. Die Verwendung von variadischen Parametern in Swift ist trivial, wie das folgende Beispiel zeigt.

func sum (_ args: Int…) -> Int var result = 0 für a in args result + = a return result sum (1, 2, 3, 4)

Die Syntax ist leicht verständlich. Um einen Parameter als variadisch zu kennzeichnen, hängen Sie dem Typ des Parameters drei Punkte an. Im Funktionshauptteil ist der Parameter variadic als Array verfügbar. Im obigen Beispiel, args ist ein Array von Int Werte.

Da Swift wissen muss, welche Argumente welchen Parametern entsprechen, muss ein variadischer Parameter der letzte Parameter sein. Dies impliziert auch, dass eine Funktion höchstens einen variablen Parameter haben kann.

Das Obige gilt auch, wenn eine Funktion Parameter mit Standardwerten hat. Der variadische Parameter sollte immer der letzte Parameter sein.

4. In-Out-Parameter

In diesem Tutorial haben Sie bereits erfahren, dass die Parameter einer Funktion Konstanten sind. Wenn Sie einen Wert an eine Funktion übergeben möchten, ändern Sie ihn in der Funktion und geben Sie ihn wieder aus der Funktion heraus. In-Out-Parameter sind das, was Sie brauchen.

Das folgende Beispiel zeigt ein Beispiel, wie In-Out-Parameter in Swift funktionieren und wie die Syntax aussieht.

func prefixString (_ string: inout String, mit Präfix: String) string = Präfix + String

Wir definieren den ersten Parameter als In-Out-Parameter durch Hinzufügen von inout Stichwort. Der zweite Parameter ist ein regulärer Parameter mit einem externen Namen von withString und ein lokaler Name von Präfix. Wie rufen wir diese Funktion auf??

var input = "Welt!" prefixString (& Eingabe, mit: "Hallo",)

Wir deklarieren eine Variable, Eingang, vom Typ String und gib es dem prefixString (_: with :) Funktion. Der zweite Parameter ist ein String-Literal. Durch Aufrufen der Funktion wird der Wert von Eingang Variable wird Hallo Welt!. Beachten Sie, dass dem ersten Argument ein kaufmännisches Und vorangestellt ist, &, um anzuzeigen, dass es sich um einen In-Out-Parameter handelt.

Es ist selbstverständlich, dass Konstanten und Literale nicht als In-Out-Parameter übergeben werden können. Der Compiler gibt einen Fehler aus, wenn Sie wie in den folgenden Beispielen beschrieben vorgehen.

Es ist offensichtlich, dass In-Out-Parameter keine Standardwerte haben oder variadisch sein können. Wenn Sie diese Details vergessen, erinnert der Compiler Sie mit einem Fehler.

5. Verschachtelung

In C und Objective-C können Funktionen und Methoden nicht verschachtelt werden. In Swift sind verschachtelte Funktionen jedoch recht häufig. Die Funktionen, die wir in diesem und im vorherigen Artikel gesehen haben, sind Beispiele für globale Funktionen - sie sind im globalen Bereich definiert.

Wenn wir eine Funktion innerhalb einer globalen Funktion definieren, bezeichnen wir diese Funktion als verschachtelte Funktion. Eine verschachtelte Funktion hat Zugriff auf die Werte, die in der umgebenden Funktion definiert sind. Sehen Sie sich das folgende Beispiel an, um dies besser zu verstehen.

func printMessage (_ message: String) let a = "Hallo Welt" func printHelloWorld () print (a)

Während die Funktionen in diesem Beispiel nicht besonders nützlich sind, veranschaulichen sie die Idee von verschachtelten Funktionen und der Erfassung von Werten. Das printHelloWorld () Funktion ist nur von innerhalb der printMessage (_ :) Funktion.

Wie im Beispiel dargestellt, wird der printHelloWorld () Funktion hat Zugriff auf die Konstante ein. Der Wert wird von der verschachtelten Funktion erfasst und kann daher von dieser Funktion aus aufgerufen werden. Swift kümmert sich um die Erfassung von Werten, einschließlich der Verwaltung der Speicherung dieser Werte.

6. Funktionsarten

Funktionen als Parameter

Im vorigen Artikel haben wir kurz auf Funktionstypen eingegangen. Eine Funktion hat einen bestimmten Typ, der sich aus den Parametertypen der Funktion und ihrem Rückgabetyp zusammensetzt. Das printMessage (_ :) Funktion ist beispielsweise vom Typ (String) -> (). Erinnere dich daran () symbolisiert Leere, das entspricht einem leeren Tupel.

Da jede Funktion einen Typ hat, ist es möglich, eine Funktion zu definieren, die eine andere Funktion als Parameter akzeptiert. Das folgende Beispiel zeigt, wie das funktioniert.

func printMessage (_ message: String) print (message) func printMessage (_ message: String, mit Funktion: (String) -> ()) function (message) let myMessage = "Hallo, Welt!" printMessage (myMessage, mit: printMessage)

Das printMessage (_: with :) Die Funktion akzeptiert einen String als ersten Parameter und eine Funktion des Typs (String) -> () als zweiten Parameter. Im Rumpf der Funktion wird die Funktion, die wir übergeben, mit der Funktion aufgerufen Botschaft Streit.

Das Beispiel zeigt auch, wie wir das aufrufen können printMessage (_: with :) Funktion. Das meine Nachricht Die Konstante wird als erstes Argument übergeben und das printMessage (_ :) Funktion als zweites Argument. Wie cool ist das?

Funktionen als Rückgabetypen

Es ist auch möglich, eine Funktion von einer Funktion zurückzugeben. Das nächste Beispiel ist ein wenig durchdacht, veranschaulicht aber, wie die Syntax aussieht.

func compute (_ add: Bool) -> (Int, Int) -> Int func add (_ a: Int, _ b: Int) -> Int return a + b func subtrahieren (_ a: Int, _ b: Int) -> Int return a - b wenn der Zusatz return add else return subtract let computeFunction = compute (true) let result = computeFunction (1, 2) print (Ergebnis)

Das berechnen(_:) function akzeptiert einen booleschen Wert und gibt eine Funktion vom Typ zurück (Int, Int) -> Int. Das berechnen(_:) Funktion enthält zwei verschachtelte Funktionen, die ebenfalls vom Typ sind (Int, Int) -> Int, hinzufügen(_:_:) und subtrahieren(_:_:).

Das berechnen(_:) Die Funktion gibt einen Verweis auf die hinzufügen(_:_:) oder der subtrahieren(_:_:) Funktion, basierend auf dem Wert von Zusatz Parameter.

Das Beispiel zeigt auch, wie Sie das verwenden berechnen(_:) Funktion. Wir speichern einen Verweis auf die Funktion, die vom zurückgegeben wird berechnen(_:) Funktion in der computeFunction Konstante. Wir rufen dann die in gespeicherte Funktion auf computeFunction, vorbei 1 und 2, Speichern Sie das Ergebnis in der Ergebnis konstant und drucken Sie den Wert von Ergebnis in der Standardausgabe. Das Beispiel mag komplex aussehen, ist aber eigentlich leicht zu verstehen, wenn Sie wissen, was los ist.

Fazit

Sie sollten jetzt ein gutes Verständnis dafür haben, wie Funktionen in Swift funktionieren und was Sie damit tun können. Funktionen sind für die Swift-Sprache von grundlegender Bedeutung, und Sie werden sie beim Arbeiten mit Swift ausgiebig verwenden.

Im nächsten Artikel tauchen wir zuerst in Schließungen ein - ein mächtiges Konstrukt, das an Blöcke in C und Objective-C, Schließungen in JavaScript und Lambdas in Ruby erinnert.

Wenn Sie wissen möchten, wie Sie mit Swift 3 echte Apps programmieren können, lesen Sie unseren Kurs Erstellen von iOS-Apps mit Swift 3. Ob Sie nun mit der iOS-App vertraut sind oder ob Sie von Objective-C auf den Wechsel wechseln möchten Natürlich erhalten Sie mit Swift einen Einstieg in die App-Entwicklung.