Funzioni Flashcards

1
Q

Definire e richiamare le funzioni

A

Quando si definisce una funzione, è possibile definire opzionalmente uno o più nomi , i valori digitati che la funzione prende come input (noto come parametri), e / o un tipo di valore che la funzione restituirà come uscita (noto come tipo di ritorno).

Ogni funzione ha un nome, che descrive il compito che la funzione stessa deve eseguire. Per utilizzare una funzione, la funzione stessa deve essere “richiamata” e si devono passare ad essa valori di input (noto come argomenti), che corrispondono ai tipi di parametri della funzione.

La funzione nell’esempio qui sotto (greetingForPerson), ha un nome che descrive il “compito” della funzione stessa, cioè prendere il nome di una persona come input e restituire un messaggio di saluto per quella persona. Per fare questo, si definisce un parametro, un ingresso valore stringa denominato personName e un tipo di dato restituito (in questo caso String), che conterrà un messaggio di saluto per quella persona:

func sayHello(personName: String) -> String {<br></br>let greeting = “Hello, ” + personName + “!”<br></br>return greeting<br></br>}

Si indica il tipo di funzione di ritorno con la freccia ” ->” , che è seguito dal nome del tipo di dato restituito.

La definizione descrive ciò che la funzione fa, che cosa ci si aspetta di ricevere e quello che restituisce quando è terminata:

println(sayHello(“Anna”))<br></br>// prints “Hello, Anna!”<br></br>println(sayHello(“Brian”))<br></br>// prints “Hello, Brian!”

Si chiama la funzione sayHello passando un valore string tra parentesi, come sayHello (“Anna”). Poiché la funzione restituisce un valore String, sayHello può essere inclusa in una chiamata alla funzione println per stampare la stringa e vedere il suo valore di ritorno, come mostrato sopra.

Il corpo della funzione sayHello inizia definendo una nuova stringa costante e imposta a un semplice messaggio di saluto per personName. Questo saluto è poi passato nuovamente fuori la funzione utilizzando la parola chiave return. Appena saluto di ritorno viene chiamato, la funzione termina la sua esecuzione e restituisce il valore corrente di saluto.

È possibile chiamare la funzione sayHello più volte con valori di input diversi. L’esempio sopra mostra cosa succede se viene chiamato con un valore di ingresso di “Anna”, e un valore di ingresso “Brian”. La funzione restituisce un saluto su misura in ogni caso.

Per semplificare il corpo di questa funzione,si può procedere come segue:

“func sayHelloAgain(personName: String) -> String {<br></br>return “Hello again, ” + personName + “!”<br></br>}<br></br>println(sayHelloAgain(“Anna”))<br></br>// prints “Hello again, Anna!”

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Parametri funzionali e valori di ritorno

A

In Swift i parametri di funzione e i valori di ritorno sono estremamente flessibili. È possibile definire qualsiasi cosa, da una semplice funzione di utilità con un unico parametro senza nome ad una funzione complessa con i nomi dei parametri espressivi e diverse opzioni per i parametri stessi.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Più parametri di input

A

Le funzioni possono avere più parametri di input, scritti fra parentesi nella funzione, separati da virgole.

Questa funzione richiede un inizio e un indice di fine per un intervallo semiaperto:

“func halfOpenRangeLength(start: Int, end: Int) -> Int {<br></br>return end – start<br></br>}<br></br>println(halfOpenRangeLength(1, 10))<br></br>// prints “9”

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Funzioni senza parametri

A

Non si è tenuti a definire parametri di input per le funzioni. Eccone una senza parametri di input, che restituisce sempre lo stesso messaggio String ogni volta che viene chiamata:

func sayHelloWorld() -> String {<br></br>return “hello, world”<br></br>}<br></br>println(sayHelloWorld())<br></br>// prints “hello, world”

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Funzioni senza valore di ritorno

A

La definizione di funzione ha ancora bisogno di parentesi dopo il nome, anche se non prende in input alcun parametro. Il nome della funzione è seguito anche da una coppia vuota di parentesi quando la funzione viene chiamata.


return end – start<br></br>}<br></br>println(halfOpenRangeLength(1, 10))<br></br>// prints “9”

Poichè non c’è bisogno di restituire un valore, la definizione della funzione non include la freccia di ritorno (->) o un tipo di ritorno.

“func printAndCount(stringToPrint: String) -> Int {<br></br>println(stringToPrint)<br></br>return countElements(stringToPrint)<br></br>}<br></br>func printWithoutCounting(stringToPrint: String) {<br></br>printAndCount(stringToPrint)<br></br>}<br></br>printAndCount(“hello, world”)<br></br>// prints “hello, world” and returns a value of 12<br></br>printWithoutCounting(“hello, world”)<br></br>// prints “hello, world” but does not return a value

La prima funzione, printAndCount, stampa una stringa, e poi restituisce il numero di caratteri come un int. La seconda funzione, printWithoutCounting, chiama la prima funzione, ma ignora il suo valore di ritorno. Quando la seconda funzione viene chiamata, il messaggio viene ancora stampato dalla prima funzione, ma il valore restituito non viene utilizzato.

Funzioni con più valori di ritorno
È possibile utilizzare un tipo di tupla come tipo di ritorno di una funzione per restituire più valori come parte del valore di ritorno.

L’esempio seguente definisce una funzione che conta il numero di vocali, consonanti, e altri caratteri in una stringa, in base al set standard di vocali e consonanti utilizzati in inglese:

func count(string: String) -> (vowels: Int, consonants: Int, others: Int) {<br></br>var vowels = 0, consonants = 0, others = 0<br></br>for character in string {<br></br>switch String(character).lowercaseString {<br></br>case “a”, “e”, “i”, “o”, “u”:<br></br>++vowels<br></br>case “b”, “c”, “d”, “f”, “g”, “h”, “j”, “k”, “l”, “m”,<br></br>“n”, “p”, “q”, “r”, “s”, “t”, “v”, “w”, “x”, “y”, “z”:<br></br>++consonants<br></br>default:<br></br>++others<br></br>}<br></br>}<br></br>return (vowels, consonants, others)<br></br>}

È possibile utilizzare questa funzione di conteggio per contare i caratteri in una stringa arbitraria, e per recuperare il totale di vocali e consonanti contate come una tupla di tre valori Int denominati:

let total = count(“some arbitrary string!”)<br></br>println(“(total.vowels) vowels and (total.consonants) consonants”)<br></br>// prints “6 vowels and 13 consonants”

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Funzioni con più valori di ritorno

A

È possibile utilizzare un tipo di tupla come tipo di ritorno di una funzione per restituire più valori come parte del valore di ritorno.

L’esempio seguente definisce una funzione che conta il numero di vocali, consonanti, e altri caratteri in una stringa, in base al set standard di vocali e consonanti utilizzati in inglese:

func count(string: String) -> (vowels: Int, consonants: Int, others: Int) {<br></br>var vowels = 0, consonants = 0, others = 0<br></br>for character in string {<br></br>switch String(character).lowercaseString {<br></br>case “a”, “e”, “i”, “o”, “u”:<br></br>++vowels<br></br>case “b”, “c”, “d”, “f”, “g”, “h”, “j”, “k”, “l”, “m”,<br></br>“n”, “p”, “q”, “r”, “s”, “t”, “v”, “w”, “x”, “y”, “z”:<br></br>++consonants<br></br>default:<br></br>++others<br></br>}<br></br>}<br></br>return (vowels, consonants, others)<br></br>}

È possibile utilizzare questa funzione di conteggio per contare i caratteri in una stringa arbitraria, e per recuperare il totale di vocali e consonanti contate come una tupla di tre valori Int denominati:

let total = count(“some arbitrary string!”)<br></br>println(“(total.vowels) vowels and (total.consonants) consonants”)<br></br>// prints “6 vowels and 13 consonants”

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Function Parameter Names

A

Tutte le funzioni di cui sopra definiscono i nomi dei parametri :

func someFunction(parameterName: Int) {<br></br>// function body goes here, and can use parameterName<br></br>// to refer to the argument value for that parameter<br></br>}

Tuttavia, questi nomi dei parametri sono utilizzati solo all’interno del corpo della funzione stessa, e non possono essere utilizzati quando si chiama la funzione. Questi tipi di nomi dei parametri sono noti come i nomi dei parametri locali, perché sono disponibili solo per l’uso all’interno del corpo della funzione.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Nomi dei parametri esterni

A

A volte è utile dare un nome ad ogni parametro quando si chiama una funzione.

Se si desidera che gli utenti che utilizzeranno le vostre funzioni forniscano i nomi dei parametri quando chiamano la funzione, definire un nome per ogni parametro esterno, in aggiunta al nome del parametro locale. Si scrive un nome di parametro esterno prima del nome del parametro locale , separati da uno spazio:

func someFunction(externalParameterName localParameterName: Int) {<br></br>// function body goes here, and can use localParameterName<br></br>// to refer to the argument value for that parameter<br></br>}

Si consideri la seguente funzione, che unisce due stringhe con l’inserimento di un terzo “joiner” tra di loro:

“func join(s1: String, s2: String, joiner: String) -> String {<br></br>return s1 + joiner + s2<br></br>}

Quando si chiama questa funzione, lo scopo dei tre parametri che vengono passati alla funzione non è chiaro:

join(“hello”, “world”, “, “)<br></br>// returns “hello, world”

Per rendere tutto più chiaro, definire i nomi dei parametri esterni:

func join(string s1: String, toString s2: String, withJoiner joiner: String)<br></br>-> String {<br></br>return s1 + joiner + s2<br></br>}

In questa versione della funzione join, il primo parametro ha un nome esterno di stringa e un nome locale di s1; il secondo parametro ha un nome esterno di toString e un nome locale di s2; e il terzo parametro ha un nome esterno di withJoiner e un nome locale di joiner.

È ora possibile utilizzare questi nomi di parametri esterni per chiamare la funzione in modo chiaro e inequivocabile:

“join(string: “hello”, toString: “world”, withJoiner: “, “)<br></br>// returns “hello, world”

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Parametri per i nomi esterni

A

Se si desidera fornire un nome di parametro esterno per un parametro di una funzione, e il nome del parametro locale è già un nome appropriato da usare, non è necessario scrivere lo stesso nome due volte per quel parametro. Invece, scrivere il nome una volta, e apporre il simbolo cancelletto al parametro (#). Questo farà capire a Swift di usare quel nome come fosse il nome del parametro locale e il nome del parametro esterno.

Questo esempio definisce una funzione chiamata containsCharacter, che definisce i nomi dei parametri esterni per entrambi i parametri mettendo un # davanti ai loro nomi:

func containsCharacter(#string: String, #characterToFind: Character) -> Bool {<br></br>for character in string {<br></br>if character == characterToFind {<br></br>return true<br></br>}<br></br>}<br></br>return false<br></br>}

La scelta utilizzata per questa funzione elimina ogni ambiguità:

<sub>let containsAVee = containsCharacter(string: “aardvark”, characterToFind: “v”)<br>// containsAVee equals true, because “aardvark” contains a “v”</sub>
‌
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Valori dei parametri di default

A

È possibile definire un valore di default per ogni parametro come parte della definizione di una funzione. Se un valore di default è definito, è possibile omettere il parametro quando si chiama la funzione.

Ecco una versione della funzione join da prima, che fornisce un valore predefinito per il suo parametro joiner:

func join(string s1: String, toString s2: String,<br></br>withJoiner joiner: String = ” “) -> String {<br></br>return s1 + joiner + s2<br></br>}

Se un valore di stringa per joiner viene fornito quando la funzione join viene chiamata, il valore di stringa viene utilizzato in questo modo:

join(string: “hello”, toString: “world”, withJoiner: “-“)<br></br>// returns “hello-world”

Tuttavia, se nessun valore di joiner viene fornito quando la funzione viene chiamata, il valore predefinito di un unico spazio (“”) viene usato al posto:

“join(string: “hello”, toString: “world”)<br></br>// returns “hello world”

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Nomi esterni per i parametri con valori di default

A

Nella maggior parte dei casi, è utile fornire un nome esterno per ogni parametro con un valore predefinito. Questo assicura che l’argomento per tale parametro sia chiaro nel momento in cui la funzione viene chiamata.

Per rendere questo processo più semplice, Swift fornisce un nome esterno automatico per qualsiasi parametro default che viene definito, se non si specifica un nome esterno da soli. Il nome esterno automatico è lo stesso del nome locale, come se si fosse apposto il cancelletto prima del nome locale nel codice.

Ecco una versione della funzione join di prima, che non prevede nomi esterni per uno qualsiasi dei suoi parametri, ma comunque fornisce un valore predefinito per il suo parametro joiner:

func join(s1: String, s2: String, joiner: String = ” “) -> String {<br></br>return s1 + joiner + s2<br></br>}

In questo caso, Swift fornisce automaticamente un nome di parametro esterno a joiner per il parametro default. Il nome esterno deve essere fornito quando si chiama la funzione, rendendo lo scopo del parametro chiaro e inequivocabile:

join(“hello”, “world”, joiner: “-“)<br></br>// returns “hello-world”‌

Un parametro variadic accetta zero o più valori di un tipo specificato. Si utilizza un parametro variadic per specificare che al parametro può essere passato un numero variabile di valori di ingresso quando la funzione viene chiamata.

I valori passati ad un parametro variadic sono resi disponibili all’interno del corpo della funzione come una matrice del tipo appropriato. Ad esempio, un parametro variadic con un nome numerico di tipo double è reso disponibile all’interno del corpo della funzione sotto forma di matrice costante di tipo double [].

L’esempio seguente calcola la media aritmetica (noto anche come media) per un elenco dei numeri di qualsiasi lunghezza:

func arithmeticMean(numbers: Double…) -> Double {<br></br>var total: Double = 0<br></br>for number in numbers {<br></br>total += number<br></br>}<br></br>return total / Double(numbers.count)<br></br>}<br></br>arithmeticMean(1, 2, 3, 4, 5)<br></br>// returns 3.0, which is the arithmetic mean of these five numbers<br></br>arithmeticMean(3, 8, 19)<br></br>// returns 10.0, which is the arithmetic mean of these three numbers

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Parametri costanti e variabili

A

Parametri di funzione sono costanti per impostazione predefinita.

Tuttavia, a volte è utile per una funzione avere una copia variabile del valore di un parametro con cui lavorare.

Definire i parametri variabili anteponendo al nome del parametro con la parola chiave var:

“func alignRight(var string: String, count: Int, pad: Character) -> String {<br></br>let amountToPad = count – countElements(string)<br></br>for _ in 1…amountToPad {<br></br>string = pad + string<br></br>}<br></br>return string<br></br>}<br></br>let originalString = “hello”<br></br>let paddedString = alignRight(originalString, 10, “-“)<br></br>// paddedString is equal to “—–hello”<br></br>// originalString is still equal to “hello”

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Parametri In-Out

A

I parametri variabili, come abbiamo visto, possono essere modificati solo all’interno della funzione stessa. Se si desidera modificare il valore di un parametro e che tali modifiche persistano dopo che la chiamata della funzione è terminata, bisogna definire il parametro come parametro in-out.

Si dichiara un parametro e si antepone “inout ” al suo inizio. Un parametro in-out ha un valore che viene passato alla funzione, viene modificato nella funzione, e viene restituito dalla funzione con il valore originale modificato.

È possibile passare solo una variabile come argomento per un parametro in-out. Non è possibile passare una costante o un valore literal come argomento, perché le costanti letterali e non possono essere modificate. Puoi piazzare una e commerciale (&) direttamente prima del nome di una variabile quando si passa come argomento di un parametro inout, per indicare che può essere modificato dalla funzione.

Ecco un esempio di una funzione chiamata swapTwoInts:

func swapTwoInts(inout a: Int, inout b: Int) {<br></br>let temporaryA = a<br></br>a = b<br></br>b = temporaryA<br></br>}

La funzione swapTwoInts scambia tra di loro i valori di a e b. La funzione esegue lo scambio memorizzando il valore di una in una costante temporanea denominata temporaryA, assegnando il valore di b per una, e assegnando temporaryA a b.

È possibile chiamare la funzione swapTwoInts con due variabili di tipo int per scambiare i loro valori. Si noti che i nomi di someInt e anotherInt sono precedute da una e commerciale quando sono passati alla funzione swapTwoInts:

var someInt = 3<br></br>var anotherInt = 107<br></br>swapTwoInts(&someInt, &anotherInt)<br></br>println(“someInt is now (someInt), and anotherInt is now (anotherInt)”)<br></br>// prints “someInt is now 107, and anotherInt is now 3”

L’esempio sopra mostra che i valori originali di someInt e anotherInt vengono modificati dalla funzione swapTwoInts, anche se essi sono stati inizialmente definiti al di fuori della funzione.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Tipi di funzione

A

Ogni funzione ha un tipo specifico, composto dai tipi di parametri e il tipo di ritorno della funzione.

Per esempio:

func addTwoInts(a: Int, b: Int) -> Int {<br></br>return a + b<br></br>}<br></br>func multiplyTwoInts(a: Int, b: Int) -> Int {<br></br>return a * b<br></br>}

Questo esempio definisce due semplici funzioni matematiche chiamate addTwoInts e multiplyTwoInts. Queste funzioni, ciascuna per due valori Int, restituiscono un valore int, che è il risultato di un’ operazione matematica.

Il tipo di entrambe queste funzioni è (Int, Int) -> Int. Questo può essere letto come:

“Un tipo di funzione che ha due parametri, di tipo Int, e che restituisce un valore di tipo int.”

Ecco un altro esempio, per una funzione senza parametri e valore di ritorno:

func printHelloWorld() {<br></br>println(“hello, world”)<br></br>}

Il tipo di questa funzione è () -> (), o “. Una funzione che non ha parametri, e restituisce Void”. Le funzioni che non specificano un valore di ritorno restituiscono sempre Void, che equivale a una tupla vuota, indicata come ().

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Utilizzo dei tipi di funzione

A

È possibile utilizzare i tipi di funzione, proprio come qualsiasi altro tipo di Swift. Ad esempio, è possibile definire una costante o una variabile di un tipo di funzione e assegnare una funzione appropriata a tale variabile:

var mathFunction: (int, int) -> int = addTwoInts

Questo può essere letto come:

Definire una variabile chiamata mathFunction, che prende due valori Int e restituisce un valore Int. Impostare questa nuova variabile per fare riferimento alle addTwoInts della funzione chiamata.

La funzione addTwoInts ha lo stesso tipo di variabile mathFunction, e quindi questa assegnazione è consentita dal tipo stesso.

Ora è possibile chiamare la funzione assegnata con il nome mathFunction:

println(“Result: (mathFunction(2, 3))”)<br></br>// prints “Result: 5”

Una funzione diversa con lo stesso tipo di corrispondenza può essere assegnata alla stessa variabile, come per i tipi non-funzionali:

mathFunction = multiplyTwoInts<br></br>println(“Result: (mathFunction(2, 3))”)<br></br>// prints “Result: 6”

Come con qualsiasi altro tipo, si può lasciare a Swift il compito di dedurre il tipo quando si assegna una funzione ad una costante o variabile:

anotherMathFunction = addTwoInts<br></br>// AnotherMathFunction si deduce essere di tipo (int, int) -> int

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Tipi di funzione come tipi di parametri

A

È possibile utilizzare un tipo di funzione come (int, int) -> int come tipo di parametro per un’altra funzione.

Ecco un esempio che stampa i risultati delle funzioni matematiche di cui sopra:

func printMathResult(mathFunction: (Int, Int) -> Int, a: Int, b: Int) {<br></br>println(“Result: (mathFunction(a, b))”)<br></br>}<br></br>printMathResult(addTwoInts, 3, 5)<br></br>// prints “Result: 8”

Questo esempio definisce una funzione chiamata printMathResult, che ha tre parametri. Il primo parametro è chiamato mathFunction, ed è di tipo (int, int) -> Int. È possibile passare qualsiasi funzione di quel tipo come argomento di questo primo parametro. Il secondo e il terzo parametro sono chiamati a e b, e sono di tipo Int. Questi sono usati come i due valori di ingresso per la funzione matematica.

Quando printMathResult viene chiamata, si passa alla funzione addTwoInts, processando i valori 3 e 5. Si chiama la funzione fornita con i valori 3 e 5, e stampa il risultato di 8.

Il ruolo di printMathResult è di stampare il risultato di una chiamata sottoforma di una funzione matematica di tipo adeguato.

17
Q

Tipi di funzione come tipi di ritorno

A

È possibile utilizzare un tipo di funzione come tipo di ritorno di un’altra funzione. A tale scopo, scrivendo un tipo di funzione completo subito dopo la freccia di ritorno (->) della funzione di ritorno.

Il prossimo esempio definisce due funzioni semplici chiamate stepForward e stepbackward. La funzione stepForward restituisce un valore maggiorato di uno rispetto al suo valore di ingresso, e la funzione stepbackward restituisce il valore d’ingresso sottratto di uno. Entrambe le funzioni hanno un tipo di (Int) -> Int:

func stepForward(input: Int) -> Int {<br></br>return input + 1<br></br>}<br></br>func stepBackward(input: Int) -> Int {<br></br>return input – 1<br></br>}

Ecco una funzione chiamata chooseStepFunction, il cui tipo di ritorno è “una funzione di tipo (Int) -> Int”. chooseStepFunction restituisce la funzione stepForward o la funzione stepbackward sulla base di un parametro booleano denominato Forward:

func chooseStepFunction (indietro: Bool) -> (Int) -> int {<br></br>tornare indietro? stepbackward: stepForward<br></br>}

È ora possibile utilizzare chooseStepFunction per ottenere una funzione che farà un passo in una direzione o l’altra (avanti o indietro):

var currentValue = 3<br></br>let moveNearerToZero = chooseStepFunction(currentValue > 0)<br></br>// moveNearerToZero now refers to the stepBackward() function

L’esempio precedente funziona se un passo positivo o negativo è necessario per spostare una variabile chiamata CurrentValue progressivamente più vicino allo zero. CurrentValue ha un valore iniziale di 3, il che significa che CurrentValue> 0 restituisce TRUE, causando chooseStepFunction per tornare alla funzione stepbackward. Un riferimento alla funzione restituito è memorizzato in una costante chiamata moveNearerToZero.

Ora che moveNearerToZero si riferisce alla funzione corretta, può essere utilizzato per contare a zero:

println(“Counting to zero:”)

// Counting to zero:

while currentValue != 0 {<br></br>println(“(currentValue)… “)<br></br>currentValue = moveNearerToZero(currentValue)<br></br>}<br></br>println(“zero!”)<br></br>// 3…<br></br>// 2…<br></br>// 1…<br></br>// zero!

18
Q

Funzioni nidificate

A

Tutte le funzioni che avete incontrato finora in questo capitolo sono esempi di funzioni globali, quindi definite in un ambito globale. È inoltre possibile definire funzioni all’interno dei corpi di altre funzioni, conosciute come funzioni nidificate.

Le Funzioni nidificate sono nascoste dal mondo esterno per impostazione predefinita, possono quindi essere chiamate e utilizzate solamente all’interno della funzione in cui sono contenute. Una funzione contenitrice può restituire una delle sue funzioni nidificate per consentire ad un’altra funzione di utilizzare la sua funzione nidificata, altrimenti non accessibile dall’esterno:

func chooseStepFunction(backwards: Bool) -> (Int) -> Int {<br></br>func stepForward(input: Int) -> Int { return input + 1 }<br></br>func stepBackward(input: Int) -> Int { return input – 1 }<br></br>return backwards ? stepBackward : stepForward<br></br>}<br></br>var currentValue = -4<br></br>let moveNearerToZero = chooseStepFunction(currentValue > 0)<br></br>// moveNearerToZero now refers to the nested stepForward() function<br></br>while currentValue != 0 {<br></br>println(“(currentValue)… “)<br></br>currentValue = moveNearerToZero(currentValue)<br></br>}<br></br>println(“zero!”)<br></br>// -4…<br></br>// -3…<br></br>// -2…<br></br>// -1…<br></br>// zero!

19
Q

Closures

A

Le closures sono blocchi autonomi che possono essere passati a diverse funzioni e utilizzati nel codice. Le closures in Swift sono simili ai blocchi in C e Objective-C.

Le closures sono in grado di catturare e memorizzare riferimenti ad eventuali costanti e variabili nel contesto in cui sono definite. Questa prassi è nota come “chiusura” di quelle costanti e variabili, da qui il nome di “closures”.

Le funzioni globali e nidificate, come già introdotto, sono in realtà casi particolari di chiusure. Possono assumere una di queste forme:

Le funzioni globali sono chiusure che hanno un nome e non catturano alcun valore.

Le funzioni nidificate sono chiusure che hanno un nome e sono in grado di catturare i valori dalla funzione che le racchiude.

Le espressioni di chiusura di Swift hanno uno stile chiaro e pulito, grazie alle ottimizzazioni che incoraggiano a scrivere una sintassi breve ed impeccabile in tutti gli scenari di programmazione. Queste ottimizzazioni includono:

Deduzione automatica dei tipi di ritorno in base al contesto
Nomi degli argomenti ben distinti
Auto-completamento della sintassi di chiusura.

20
Q

Closure Expressions

A

Le funzioni nidificate, come già introdotto, sono un comodo mezzo di denominazione e definizione di blocchi indipendenti di codice come parte di una funzione più ampia. Tuttavia, a volte è utile scrivere versioni brevi di costrutti di funzione simile senza dichiararne e il nome completo. Questo è particolarmente vero quando si lavora con le funzioni che richiedono altre funzioni come argomento.

Le espressioni di chiusura sono un modo per scrivere chiusure inline in una sintassi breve e concisa. Queste espressioni forniscono diverse ottimizzazioni di sintassi per la scrittura delle closures nella loro forma più semplice, senza perdite di purezza o errori accidentali. Gli esempi seguenti illustrano espressioni di chiusura ottimizzate, ognuna delle quali esprime la stessa funzionalità in maniera più succinta.

21
Q

The Sort Function

A

La libreria standard di Swift offre una funzione chiamata “sort”(ordina), che appunto ordina una serie di valori di tipo noto, in base all’uscita fornita da una chiusura. Una volta completato il processo di ordinamento, la funzione restituisce un nuovo array dello stesso tipo e dimensione di quello vecchio, con i suoi elementi ordinati in modo corretto.

La closures utilizzata nell’esempio qui sotto è utilizzata per ordinare un array di valori di tipo string in ordine alfabetico inverso. Ecco la matrice iniziale da ordinare:

far nomi = [“Chris”, “Alex”, “Ewa”, “Barry”, “Daniella”]

La funzione di ordinamento accetta due argomenti:

Un array di valori di tipo noto (string).

Una chiusura che prende due argomenti dello stesso tipo, comead esempio il contenuto di una matrice, e restituisce un valore Bool per dire se il primo valore dovrebbe apparire prima o dopo il secondo valore, una volta che i valori stessi sono ordinati. La chiusura di ordinamento deve restituire true se il primo valore dovrebbe apparire prima del secondo valore, e false nel caso in cui il valore debba apparire dopo il secondo valore.

Questo esempio è un Ordinamento di un array di valori stringa, quindi la chiusura di smistamento deve essere una funzione di tipo (String, String) -> Bool.

Un modo per garantire la chiusura di ordinamento è quello di scrivere una normale funzione del tipo corretto e di passarla come secondo parametro della funzione di ordinamento:

func backwards(s1: String, s2: String) -\> Bool {
return s1 \> s2
}
var reversed = sort(names, backwards)
// reversed is equal to [“Ewa”, “Daniella”, “Chris”, “Barry”, “Alex”]

Se la prima stringa (s1) è maggiore della seconda stringa (s2), la funzione restituirà vero, indicando che s1 dovrebbe comparire davanti s2 nell’ array ordinato. Per i caratteri in stringhe, “superiore” significa “appare più avanti in alfabeto che”. Ciò significa che la lettera “B” è “superiore” la lettera “A”, e la stringa “Tom” è superiore della stringa “Tim”e così via.

Tuttavia, questo è un modo piuttosto complesso per scrivere ciò che è essenzialmente una funzione riassumibile nella singola espressione (a> b).

22
Q

Closure Expression Syntax

A

Le sintassi delle espressioni di chiusura in generale si presentano in questa forma:

{(Parametri) -> tipo di ritorno in<br></br>dichiarazioni<br></br>}

Le sintassi delle espressioni di chiusura possono utilizzare parametri costanti, parametri variabili e parametri inout. Si possono utilizzare Tuple come tipi di parametri.

L’esempio seguente mostra un’espressione di chiusura della funzione di ordinamento inverso:

invertito = sort (nomi, {(s1: String, s2: String) -> Bool in<br></br>return s1> s2<br></br>})

Si noti che la dichiarazione di parametri e del tipo di ritorno per questa closure è identica alla dichiarazione dalla funzione all’inverso. In entrambi i casi, è scritto come (s1: String, s2: String) -> Bool. Tuttavia, per l’espressione di chiusura in linea, i parametri e il tipo restituito sono scritte all’interno delle parentesi graffe, non al di fuori di esse.

L’inizio del corpo della chiusura è introdotto dalla parola chiave “in”. Questa parola indica che la definizione di parametri e dei tipi di ritorno della chiusura è terminata e il corpo vero e proprio della chiusura sta per iniziare.

Poiché il corpo della chiusura è relativamente breve, può anche essere scritto su una sola riga:

reverse = sort (nomi, {(s1: String, s2: String) -> Bool in cambio s1> s2})

Questo dimostra che la chiamata generale per la funzione di ordinamento è rimasta la stessa. L’intera serie di argomenti è ancora contenuta in due parentesi. Tuttavia, uno di questi è ora una chiusura inline.

23
Q

Inferring Type From Context

A

Poiché la chiusura di ordinamento viene passato come argomento a una funzione, Swift può dedurre i tipi dei suoi parametri e il tipo del valore restituito . Questo parametro è in attesa di una funzione di tipo (String, String) -> Bool. Ciò significa che la stringa, di tipo String, e Bool non deve necessariamente essere scritta come parte della definizione dell’espressione chiusura. Poiché tutti i tipi di dato possono essere dedotti, la freccia di ritorno (->) e le parentesi intorno ai nomi dei parametri possono anche essere omesse:

invertito = sort (nomi, {s1, s2 in cambio s1> s2})

È sempre possibile dedurre i tipi dei parametri e il tipo di ritorno quando si passa una chiusura a una funzione come espressione di chiusura di linea. E’ raramente necessario scrivere una chiusura in linea nella sua forma completa.

Tuttavia, se lo si desidera, è possibile scriverla in forma completa; così facendo si rende il codice maggiormente leggibile nel caso in cui bisogna apportarvi qualche modifica in futuro. Nel caso della funzione di ordinamento, lo scopo della chiusura è evidente dal fatto che lo smistamento dei valori è avvenuto.

24
Q

Implicit Returns from Single-Expression Closures

A

Chiusure ad espressione singola possono implicitamente restituire il risultato della loro singola espressione omettendo “return” dalla loro dichiarazione, come in questa versione dell’esempio precedente:

invertito = ordinamento (nomi, {s1, s2 in s1> s2})
In questa riga, il tipo di funzione del secondo argomento, specifica che il valore Bool deve essere restituito dalla chiusura. Poiché il corpo della chiusura contiene una singola espressione (s1> s2) che restituisce un valore Bool, non c’è ambiguità, e return può essere omesso.

25
Q

Shorthand Argument Names

A

Swift fornisce automaticamente i nomi degli argomenti abbreviati all’inline di chiusura, che può essere utilizzato per fare riferimento ai valori degli argomenti della chiusura con i nomi $ 0, $ 1, $ 2, e così via.

Se si utilizzano questi nomi degli argomenti all’interno di un’espressione di chiusura, è possibile omettere la definizione del tipo della lista, e il numero e il tipo dei nomi degli argomenti in essa contenuti, i quali saranno dedotti dal tipo di funzione prevista. Ad esempio si può procedere nel seguente modo:

reverse = sort (nomi, {$ 0> $ 1})

Qui, $ 0 e 1 $ si riferiscono al primo e secondo argomento String della chiusura.

26
Q

Operator Functions

A

In realtà c’è un modo ancora più breve di scrivere l’espressione di chiusura di cui sopra. Il tipo String di Swift definisce la sua applicazione specifica (per quanto riguarda una stringa) dell’operatore di maggiore (>) come una funzione che ha due parametri di tipo String, e restituisce un valore di tipo Bool. Ciò corrisponde esattamente al tipo di funzione necessaria per il secondo parametro della funzione di ordinamento. Pertanto, si può semplicemente passare l’operatore di maggiore, e Swift deduce che si desidera utilizzarlo per ordinare dei valori di tipo string:

invertito = sort (nomi,>)

27
Q

Closures 2

A

func someFunctionThatTakesAClosure(closure: () -> ()) {<br></br>// function body goes here<br></br>}

// here’s how you call this function without using a trailing closure:

someFunctionThatTakesAClosure({<br></br>// closure’s body goes here<br></br>})

// here’s how you call this function with a trailing closure instead:

someFunctionThatTakesAClosure() {<br></br>// trailing closure’s body goes here<br></br>}

Le chiusure finali sono utili quando la chiusura è sufficientemente lunga da non essere possibile scriverla su una singola riga. Ad esempio, gli array in Swift hanno un metodo map che prende un’espressione di chiusura come unico argomento. La chiusura viene chiamata una volta per ogni elemento dell’array e restituisce un valore map alternativo (anche di altro tipo) per quella voce. La natura della mappatura e il tipo del valore restituito è lasciata alla chiusura.

Dopo aver applicato la chiusura fornita a ciascun elemento della matrice, il metodo map restituisce un nuovo array contenente tutti i nuovi valori mappati, nello stesso ordine dei valori corrispondenti nell’array originale.

Ecco come possiamo utilizzare il metodo map con una chiusura finale per convertire una matrice di valori Int in un array di valori stringa. La matrice [16, 58, 510] viene utilizzata per creare il nuovo array [“OneSix”, “FiveEight”, “FiveOneZero”]:

let digitNames = [

0: “Zero”, 1: “One”, 2: “Two”, 3: “Three”, 4: “Four”,<br></br>5: “Five”, 6: “Six”, 7: “Seven”, 8: “Eight”, 9: “Nine”<br></br>]<br></br>let numbers = [16, 58, 510]

Il codice appena visto crea un dizionario di mapping tra le cifre intere e le versioni in lingua inglese dei loro nomi. Esso definisce anche una serie di numeri interi, pronti per essere convertito in stringhe.

È ora possibile utilizzare la matrice per creare un array di valori String, passando un’espressione di chiusura alla mappa il metodo della matrice come una chiusura finale.

let strings = numbers.map {<br></br>(var number) -> String in<br></br>var output = “”<br></br>while number > 0 {<br></br>output = digitNames[number % 10]! + output<br></br>number /= 10<br></br>}<br></br>return output<br></br>}<br></br>// strings is inferred to be of type String[]<br></br>// its value is [“OneSix”, “FiveEight”, “FiveOneZero”]

La funzione map richiama l’espressione di chiusura una volta per ogni elemento dell’array. Non è necessario specificare il tipo di parametro di ingresso della chiusura, perché questo può essere dedotto dai valori nella matrice da mappare.

In questo esempio, il parametro numero di chiusura è definito come variabile, in modo che il valore possa essere modificato all’interno del corpo di chiusura, piuttosto che dichiarare una nuova variabile e assegnando il valore numerico passato ad esso. L’espressione di chiusura specifica anche un tipo di ritorno String, per indicare il tipo che verrà memorizzato nella matrice di uscita.

L’espressione di chiusura costruisce una stringa chiamata in uscita ogni volta che viene eseguita. Calcola l’ultima cifra del numero utilizzando l’operatore resto (numero% 10), e usa questa cifra per cercare una stringa appropriata nelle digitNames.

La stringa recuperata dal dizionario digitNames viene aggiunta, costruendo efficacemente una versione stringa del numero, procedendo al contrario. (Il numero espressione% 10 dà un valore di 6 per 16, 8 per 58, e 0 per 510.)

La variabile numero viene poi divisa per 10. Poiché si tratta di un numero intero, viene arrotondato per difetto durante la divisione, quindi 16 diventa 1, 58 diventa 5, e 510 diventa 51.

Il processo viene ripetuto fino a quando il numero / = 10 è uguale a 0, il punto in cui la stringa di output viene restituita dalla chiusura, e viene aggiunto alla matrice di uscita dalla funzione mappa.

28
Q

Catturare valori

A

Una chiusura può catturare costanti e variabili dal contesto circostante in cui è definita. La chiusura può consultare e modificare i valori di tali costanti e variabili, anche se la portata originale che definisce le costanti e variabili non esiste più.

La forma più semplice di una chiusura in Swift è una funzione nidificata, scritta nel corpo di un’altra funzione. Una funzione annidata può catturare uno qualsiasi degli argomenti della sua funzione esterna e può anche catturare eventuali costanti e variabili definite all’interno della funzione esterna.

Ecco un esempio di una funzione chiamata makeIncrementor, che contiene una funzione annidata chiamata incrementator. La funzione incrementator annidata cattura due valori, RunningTotal e quantità, dal suo contesto circostante. Dopo la cattura di questi valori, incrementator viene restituito da makeIncrementor come una chiusura che aumenta RunningTotal ogni volta che viene chiamato.

func makeIncrementor(forIncrement amount: Int) -> () -> Int {<br></br>var runningTotal = 0<br></br>func incrementor() -> Int {<br></br>runningTotal += amount<br></br>return runningTotal<br></br>}<br></br>return incrementor<br></br>}

Il tipo di ritorno di makeIncrementor è () -> Int. Ciò significa che restituisce una funzione, invece di un valore semplice. La funzione return non ha parametri, e restituisce un valore Int ogni volta che viene chiamato.

La funzione makeIncrementor definisce una variabile intera chiamata RunningTotal, inizializzata con il valore 0.

La funzione makeIncrementor ha un solo parametro Int con un nome esterno di forIncrement, e un nome locale di import. Il valore argomento passato a questo parametro specifica di quanto RunningTotal dovrebbe essere incrementato di volta in volta che la funzione viene chiamata.

makeIncrementor definisce una funzione annidata chiamata incrementator, che esegue l’effettiva azione di incremento. Questa funzione aggiunge semplicemente “importo ” a RunningTotal, e restituisce il risultato.

Se considerati separatamente, la funzione incrementator nidificata potrebbe sembrare insolita:

func incrementor() -> Int {<br></br>runningTotal += amount<br></br>return runningTotal<br></br>}

La funzione incrementator non ha parametri, ma si riferisce ad RunningTotal e importo dall’interno suo corpo della funzione. Lo fa catturando i valori attuali di RunningTotal e quantità dalla sua funzione circostante e il loro utilizzo nel proprio corpo della funzione.

Poiché non modifica quantità, incrementator effettivamente cattura e memorizza una copia del valore memorizzato in quantità. Questo valore viene memorizzato insieme alla nuova funzione incrementator.

Tuttavia, poichè modifica la variabile RunningTotal ogni volta che viene chiamato, incrementator cattura un riferimento alla variabile RunningTotal corrente e non solo una copia del suo valore iniziale. Catturare un riferimento assicura che RunningTotal non scompaia quando la chiamata a makeIncrementor finisce, e assicura che RunningTotal continuerà ad essere disponibile la prossima volta che la funzione incrementator verrà chiamata.

Ecco un esempio di makeIncrementor in azione:

Chiamando la funzione più volte mostra questo comportamento in azione:

incrementByTen()<br></br>// returns a value of 10<br></br>incrementByTen()<br></br>// returns a value of 20<br></br>incrementByTen()<br></br>// returns a value of 30

Se si crea un altro incrementator, avrà un proprio riferimento memorizzato in una nuova variabile RunningTotal separato. Nell’esempio che segue, incrementBySeven cattura un riferimento ad una nuova variabile RunningTotal non collegato a quello catturato da incrementByTen:

let incrementBySeven = makeIncrementor(forIncrement: 7)<br></br>incrementBySeven()<br></br>// returns a value of 7<br></br>incrementByTen()<br></br>// returns a value of 40

29
Q

Chiusure sono tipi di riferimento

A

Ogni volta che si assegna una funzione o una chiusura di una costante o una variabile, in realtà si sta specificando che la costante o la variabile sia un riferimento alla funzione o la chiusura. Nell’esempio precedente, è la scelta di chiusura che incrementByTen riferisce a quella è costante, e non al contenuto della chiusura stessa.

Questo significa anche che se si assegna una chiusura di due costanti o variabili diverse, entrambe le costanti o variabili si riferiscono alla stessa chiusura:

let alsoIncrementByTen = incrementByTen<br></br>alsoIncrementByTen()<br></br>// returns a value of 50

30
Q

Enumerations

A

Una enumerazione definisce un tipo comune per un gruppo di valori.

Se avete familiarità con C, saprete che in C le enumerazioni servono per assegnare nomi relativi ad una serie di valori interi. In Swift le enumerazioni sono molto più flessibil. Se un valore (inteso come valore “grezzo”) viene fornito per ciascun membro di enumerazione, il valore può essere una stringa, un carattere o un valore di qualsiasi tipo (intero o in virgola mobile).

In alternativa, le enumerazioni possono specificare valori di qualsiasi tipo, da memorizzare con ciascun valore di tipo diverso , come se creassimo un dizionario che associa due parole in lingue diverse.

Le enumerazioni di Swift adottano molte caratteristiche tradizionalmente supportate solo dalle classi, come ad esempio le proprietà calcolate per fornire ulteriori informazioni sul valore corrente nei vari conteggi, e metodi di istanza per fornire funzionalità legate ai valori di enumerazione. Le enumerazioni possono anche definire initializers, fornendo un valore iniziale.

31
Q

Enumeration Syntax

A

Si inizializza un’ enumerazioni con la parola chiave “enum”, inserendo il tutto all’interno di una coppia di parentesi graffe:

enum SomeEnumeration {<br></br>// enumeration definition goes here<br></br>}<br></br>Here’s an example for the four main points of a compass:

enum CompassPoint {<br></br>case North<br></br>case South<br></br>case East<br></br>case West<br></br>}

I valori definiti in una enumerazione (come Nord, Sud, Est, Ovest) sono i valori dei membri di tale enumerazione. La parola chiave “case” indica che una nuova linea di valori sta per essere definita.

Eventuali valori multipli dei membri dell’enumerazione possono apparire su una sola riga, separati da virgole:

enum Planet {<br></br>case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune<br></br>}

Ogni definizione di enumerazione definisce un nuovo tipo. Come altri tipi di Swift, i loro nomi (come CompassPoint e Planet) devono iniziare con una lettera maiuscola. E’ consigliato dare tipi di enumerazione singolari piuttosto che plurali, in modo che vengano percepiti in modo evidente:

var directionToHead = CompassPoint.West

Il tipo di directionToHead si inferisce quando viene inizializzato con uno dei possibili valori di CompassPoint. Una volta che directionToHead è dichiarato come CompassPoint, è possibile impostare un valore diverso di CompassPoint utilizzando una sintassi breve:

directionToHead = .East

Essendo il tipo di directionToHead già noto, lo si può omettere quando si va ad impostare il suo valore. Questo rende il codice molto più leggibile.
Abbinamento dei valori di enumerazione con un’istruzione switch
È possibile abbinare i valori di enumerazione individuali con un’istruzione switch:

directionToHead = .South<br></br>switch directionToHead {<br></br>case .North:<br></br>println(“Lots of planets have a north”)<br></br>case .South:<br></br>println(“Watch out for penguins”)<br></br>case .East:<br></br>println(“Where the sun rises”)<br></br>case .West:<br></br>println(“Where the skies are blue”)<br></br>}<br></br>// prints “Watch out for penguins”

Si può leggere questo codice come:

Si consideri il valore di directionToHead. Nel caso in cui esso sia uguale a .North, stampa “Un sacco di pianeti hanno un nord”. Nel caso in cui esso è uguale a .South, stampare “Fare attenzione ai pinguini”.

… E così via.

Come descritto in Flow Control, un’istruzione switch deve essere esaustiva nel considerare i membri di una enumerazione. Se il case per .West viene omesso, questo codice non verrà compilato, perché non considera la lista completa dei membri CompassPoint. Richiedere esaustività nella stesura del codiceassicura che i membri di enumerazione non vengano accidentalmente omessi.

Quando non è opportuno prevedere un case per ogni membro di enumerazione, è possibile fornire un case di default per coprire tutti i membri che non sono stati dichiarati in modo esplicito:

let somePlanet = Planet.Earth<br></br>switch somePlanet {<br></br>case .Earth:<br></br>println(“Mostly harmless”)<br></br>default:<br></br>println(“Not a safe place for humans”)<br></br>}<br></br>// prints “Mostly harmless”

32
Q
A
33
Q
A
34
Q
A