Nel precedente articolo, abbiamo esplorato le basi delle funzioni in Swift. Le funzioni, tuttavia, hanno molto di più da offrire. In questo articolo, continuiamo la nostra esplorazione delle funzioni e analizziamo i parametri delle funzioni, l'annidamento e i tipi.
Cerchiamo di rivisitare uno degli esempi del precedente articolo. Il printMessage (messaggio :)
la funzione definisce un parametro, Messaggio
.
func printMessage (message: String) print (messaggio)
Assegnamo un nome, Messaggio
, al parametro e utilizzare questo nome quando chiamiamo la funzione.
printMessage (messaggio: "Ciao, mondo!")
Ma notiamo che usiamo anche lo stesso nome per fare riferimento al valore del parametro nel corpo della funzione.
func printMessage (message: String) print (messaggio)
In Swift, un parametro ha sempre a Locale nome del parametro, e opzionalmente ha un esterno nome del parametro. Nell'esempio, i nomi dei parametri locali ed esterni sono identici.
A partire da Swift 3, il team di Swift ha definito un chiaro insieme di linee guida API. Non entrerò in queste linee guida in questo tutorial, ma voglio sottolineare che la definizione di printMessage (messaggio :)
la funzione si discosta da queste linee guida. Il nome della funzione contiene la parola Messaggio
, e il parametro è anche nominato Messaggio
. In altre parole, ci stiamo ripetendo.
Sarebbe più elegante se potessimo invocare il printMessage (messaggio :)
funzione senza il Messaggio
parola chiave. Questo è quello che ho in mente.
printMessage ("Ciao, mondo!")
Questo è possibile ed è più in linea con le linee guida dell'API Swift. Ma cosa è diverso? La differenza è facile da individuare se diamo un'occhiata alla definizione della funzione aggiornata. L'esempio aggiornato rivela anche di più sull'anatomia delle funzioni in Swift.
func printMessage (_ message: String) print (message)
In una definizione di funzione, ogni parametro è definito da un nome di parametro esterno, un nome di parametro locale, due punti e il tipo del parametro. Se i nomi dei parametri locali ed esterni sono identici, scriviamo solo il nome del parametro una volta. Questo è il motivo per cui il primo esempio definisce un nome parametro, Messaggio
.
Se non vogliamo assegnare un nome di parametro esterno a un parametro, usiamo il comando _
, un trattino basso Questo informa il compilatore che il parametro non ha un nome di parametro esterno e ciò significa che possiamo omettere il nome del parametro quando la funzione è invocata.
Objective-C è noto per i suoi lunghi nomi di metodi. Mentre questo può sembrare goffo e inelegante per gli estranei, rende i metodi facili da capire e, se scelti bene, molto descrittivi. Il team di Swift ha compreso questo vantaggio e ha introdotto i nomi dei parametri esterni dal primo giorno.
Quando una funzione accetta diversi parametri, non è sempre ovvio quale argomento corrisponda a quale parametro. Dai un'occhiata al seguente esempio per capire meglio il problema. Si noti che i parametri non hanno un nome di parametro esterno.
func power (_ a: Int, _ b: Int) -> Int var result = a per _ in 1 ...Il
energia(_:_:)
la funzione aumenta il valore diun
dall'esponenteB
. Entrambi i parametri sono di tipoInt
. Mentre molte persone passano intuitivamente il valore di base come primo argomento e l'esponente come secondo argomento, questo non è chiaro dal tipo, nome o firma della funzione. Come abbiamo visto nel precedente articolo, invocare la funzione è semplice.potenza (2, 3)Per evitare confusione, possiamo dare i parametri di una funzione nomi esterni. Possiamo quindi utilizzare questi nomi esterni quando la funzione viene chiamata per indicare in modo non ambiguo quale argomento corrisponde a quale parametro. Dai un'occhiata all'esempio aggiornato di seguito.
func power (base a: Int, esponente b: Int) -> Int var result = a per _ in 1 ...Si noti che il corpo della funzione non è cambiato poiché i nomi locali non sono stati modificati. Tuttavia, quando invochiamo la funzione aggiornata, la differenza è chiara e il risultato è meno confuso.
potenza (base: 2, esponente: 3)Mentre i tipi di entrambe le funzioni sono identici,
(Int, Int) -> Int
, le funzioni sono diverse. In altre parole, la seconda funzione non è una ridichiarazione della prima funzione. La sintassi per invocare la seconda funzione potrebbe ricordare l'Objective-C. Non solo gli argomenti sono chiaramente descritti, ma la combinazione di nomi di funzioni e parametri descrive anche lo scopo della funzione.In alcuni casi, si desidera utilizzare lo stesso nome per il nome del parametro locale e esterno. Questo è possibile e non è necessario digitare il nome del parametro due volte. Nell'esempio seguente, usiamo
base
eesponente
come i nomi dei parametri locali ed esterni.func power (base: Int, esponente: Int) -> Int var result = base per _ in 1 ...Definendo un nome per ciascun parametro, il nome del parametro funge da nome locale ed esterno del parametro. Ciò significa anche che dobbiamo aggiornare il corpo della funzione.
È importante notare che, fornendo un nome esterno per un parametro, è necessario utilizzare tale nome quando si richiama la funzione. Questo ci porta ai valori di default.
Valori standard
Abbiamo coperto i valori dei parametri predefiniti nell'articolo precedente. Questa è la funzione che abbiamo definito in quell'articolo.
func printDate (data: Date, format: String = "YY / MM / dd") -> String let dateFormatter = DateFormatter () dateFormatter.dateFormat = format return dateFormatter.string (from: date)Cosa succede se non definiamo un nome di parametro esterno per il secondo parametro, che ha un valore predefinito?
func printDate (data: Date, _ format: String = "YY / MM / dd") -> String let dateFormatter = DateFormatter () dateFormatter.dateFormat = format return dateFormatter.string (from: date)Il compilatore non sembra preoccuparsi. Ma è questo ciò che vogliamo? È meglio definire un nome di parametro esterno su parametri opzionali (parametri con un valore predefinito) per evitare confusione e ambiguità.
Si noti che ci stiamo ripetendo di nuovo nell'esempio precedente. Non è necessario definire un nome di parametro esterno per
Data
parametro. Il prossimo esempio mostra cosaPrintDate (_: formato :)
la funzione sembrerebbe se seguissimo le linee guida dell'API Swift.func printDate (_ date: Date, format: String = "YY / MM / dd") -> String let dateFormatter = DateFormatter () dateFormatter.dateFormat = format return dateFormatter.string (from: date)Ora possiamo invocare il
FormatoData (_: formato :)
funzione senza usare ilData
etichetta per il primo parametro e con un formato data opzionale.printDate (Date ()) printDate (Date (), formato: "dd / MM / YY")2. Parametri e mutabilità
Cerchiamo di rivisitare il primo esempio di questo tutorial, il
printMessage (_ :)
funzione. Cosa succede se cambiamo il valore diMessaggio
parametro all'interno del corpo della funzione?func printMessage (_ message: String) message = "Stampa: \ (messaggio)" print (messaggio)Non ci vuole molto perché il compilatore inizi a lamentarsi.
I parametri di una funzione sono costanti. In altre parole, mentre possiamo accedere ai valori dei parametri di funzione, non possiamo cambiare il loro valore. Per ovviare a questa limitazione, dichiariamo una variabile nel corpo della funzione e usiamo invece quella variabile.
func printMessage (_ message: String) var message = message message = "Stampa: \ (messaggio)" print (messaggio)3. Parametri variabili
Mentre il termine può sembrare strano all'inizio, i parametri variadici sono comuni nella programmazione. Un parametro variadico è un parametro che accetta zero o più valori. I valori devono essere dello stesso tipo. L'uso dei parametri variadici in Swift è banale, come illustrato nell'esempio seguente.
func sum (_ args: Int ...) -> Int var result = 0 per a in args risultato + = a risultato risultato somma (1, 2, 3, 4)La sintassi è facile da capire. Per contrassegnare un parametro come variadic, aggiungi tre punti al tipo del parametro. Nel corpo della funzione, il parametro variadic è accessibile come una matrice. Nell'esempio sopra,
args
è una matrice diInt
valori.Poiché Swift deve sapere quali argomenti corrispondono a quali parametri, è necessario che un parametro variabile sia l'ultimo parametro. Implica anche che una funzione possa avere al massimo un parametro variadico.
Quanto sopra si applica anche se una funzione ha parametri con valori predefiniti. Il parametro variadic dovrebbe essere sempre l'ultimo parametro.
4. Parametri In-Out
In precedenza in questo tutorial, hai imparato che i parametri di una funzione sono costanti. Se vuoi passare un valore in una funzione, modificarlo nella funzione e passarlo di nuovo fuori dalla funzione, i parametri in-out sono ciò di cui hai bisogno.
L'esempio seguente mostra un esempio di come i parametri in-out funzionano in Swift e come si presenta la sintassi.
func prefixString (_ stringa: inout String, con prefisso: String) string = prefisso + stringaDefiniamo il primo parametro come parametro in-out aggiungendo il
dentro fuori
parola chiave. Il secondo parametro è un parametro normale con un nome esterno diwithString
e un nome locale diprefisso
. Come invochiamo questa funzione?var input = "world!" prefixString (& input, with: "Hello,")Dichiariamo una variabile,
ingresso
, di tipoStringa
e passalo alprefixString (_: con :)
funzione. Il secondo parametro è una stringa letterale. Invocando la funzione, il valore diingresso
variabile diventaCiao mondo!
. Si noti che il primo argomento è preceduto da una e commerciale,&
, per indicare che è un parametro in-out.Inutile dire che costanti e letterali non possono essere passati come parametri in-out. Il compilatore genera un errore quando si esegue come illustrato nei seguenti esempi.
È evidente che i parametri in-out non possono avere valori predefiniti o essere variadici. Se dimentichi questi dettagli, il compilatore ti ricorda gentilmente un errore.
5. Nesting
In C e Objective-C, funzioni e metodi non possono essere nidificati. In Swift, tuttavia, le funzioni annidate sono abbastanza comuni. Le funzioni che abbiamo visto in questo e nel precedente articolo sono esempi di funzioni globali: sono definite nell'ambito globale.
Quando definiamo una funzione all'interno di una funzione globale, facciamo riferimento a tale funzione come funzione annidata. Una funzione nidificata ha accesso ai valori definiti nella sua funzione di chiusura. Dai un'occhiata al seguente esempio per capire meglio questo.
func printMessage (_ message: String) let a = "hello world" func printHelloWorld () print (a)Sebbene le funzioni in questo esempio non siano molto utili, illustrano l'idea di funzioni annidate e di acquisizione dei valori. Il
printHelloWorld ()
la funzione è accessibile solo dall'interno delprintMessage (_ :)
funzione.Come illustrato nell'esempio, il
printHelloWorld ()
la funzione ha accesso alla costanteun
. Il valore viene catturato dalla funzione nidificata ed è quindi accessibile dall'interno di quella funzione. Swift si occupa di acquisire i valori, inclusa la gestione della memoria di quei valori.6. Tipi di funzioni
Funziona come parametro
Nel precedente articolo, abbiamo brevemente toccato i tipi di funzione. Una funzione ha un tipo particolare, composto dai tipi di parametri della funzione e dal suo tipo di ritorno. Il
printMessage (_ :)
la funzione, ad esempio, è di tipo(Stringa) -> ()
. Ricordatelo()
simboleggiavuoto
, che è equivalente a una tupla vuota.Poiché ogni funzione ha un tipo, è possibile definire una funzione che accetta un'altra funzione come parametro. L'esempio seguente mostra come funziona.
func printMessage (_ message: String) print (message) func printMessage (_ message: String, with function: (String) -> ()) function (message) let myMessage = "Ciao, mondo!" printMessage (myMessage, con: printMessage)Il
printMessage (_: con :)
la funzione accetta una stringa come primo parametro e una funzione di tipo(Stringa) -> ()
come il suo secondo parametro. Nel corpo della funzione, la funzione che passiamo viene invocata con ilMessaggio
discussione.L'esempio illustra anche come possiamo invocare il
printMessage (_: con :)
funzione. Ilil mio messaggio
la costante è passata come primo argomento e ilprintMessage (_ :)
funzione come secondo argomento. Quant'è fico?Funziona come Tipi di reso
È anche possibile restituire una funzione da una funzione. Il prossimo esempio è un po 'forzato, ma illustra come appare la sintassi.
func compute (_ addition: Bool) -> (Int, Int) -> Int func add (_ a: Int, _ b: Int) -> Int return a + b func sottrarre (_ a: Int, _ b: Int) -> Int return a - b if addition return add else return sottrarre let computeFunction = compute (true) let result = computeFunction (1, 2) print (result)Il
calcolare(_:)
la funzione accetta un valore booleano e restituisce una funzione di tipo(Int, Int) -> Int
. Ilcalcolare(_:)
la funzione contiene due funzioni annidate anche di tipo(Int, Int) -> Int
,Inserisci(_:_:)
esottrarre(_:_:)
.Il
calcolare(_:)
la funzione restituisce un riferimento aInserisci(_:_:)
o ilsottrarre(_:_:)
funzione, in base al valore delaggiunta
parametro.L'esempio mostra anche come usare il
calcolare(_:)
funzione. Memorizziamo un riferimento alla funzione che viene restituita dalcalcolare(_:)
funzione nelcomputeFunction
costante. Quindi invochiamo la funzione memorizzata incomputeFunction
, passando dentro1
e2
, memorizzare il risultato nel filerisultato
costante e stampa il valore dirisultato
nell'output standard. L'esempio può sembrare complesso, ma in realtà è facile da capire se sai cosa sta succedendo.Conclusione
Dovresti ora avere una buona comprensione di come funzionano le funzioni in Swift e cosa puoi fare con loro. Le funzioni sono fondamentali per la lingua Swift e le utilizzerai ampiamente quando lavori con Swift.
Nel prossimo articolo, ci immergiamo dapprima nelle chiusure: un potente costrutto che richiama blocchi in C e Objective-C, chiusure in JavaScript e lambda in Ruby.
Se vuoi imparare come usare Swift 3 per codificare le applicazioni del mondo reale, consulta il nostro corso Crea app per iOS con Swift 3. Che tu sia nuovo nello sviluppo di app per iOS o che stia cercando di passare da Objective-C, questo corso ti consentirà di iniziare con Swift per lo sviluppo di app.