Nel tuo viaggio da sviluppatore iOS saranno tante le volte in cui dovrai ripetere operazioni.
Immagina, per esempio, di voler leggere gli elementi di un’array composto da migliaia di elementi. Con gli strumenti che hai visto fino a questo momento è impensabile risolvere questo problema perché significherebbe dover scrivere riga per riga l’accesso agli elementi:
// Esempio: stampa in console tutti gli elementi presenti all'interno di un array:
let array = ["ciao", "come stai?", "hai letto questo messaggio?", "oggi parliamo di ciclo for in Swift"]
array[0]
array[1]
array[2]
// impossibile quando gli elementi sono tantissimi
Ovviamente a tutto c’è una soluzione 😂.
Il linguaggio Swift, infatti, mette a disposizione una keyword che ci permette di ripetere N volte un blocco di codice. Questa istruzione si chiama ciclo for.
Dato che ho già detto tutto, non perdiamo altro tempo e vediamo subito come utilizzare il ciclo for in Swift!
Pronto? cominciamo!
Sintassi del ciclo for-in
Il ciclo for del linguaggio Swift permette di eseguire un blocco di codice in maniera iterativa.
Cosa significa?
Immaginiamo di voler leggere e stampare gli elementi di un’array:
var arrayNomi: [String] = ["giuseppe", "matteo", "luca", "filippo"]
La sintassi di questa istruzione è abbastanza semplice. Bisogna:
- Scrivere la keyword for
- Aggiungere un simbolo _
- si legge underscore e capiremo meglio tra un attimo perché lo stiamo utilizzando
- Far seguire la keyword in
- Poi aggiungere l’array o, più in generale, la collezione che vogliamo leggere
- Ed infine aggiungere un blocco di parentesi graffe
Dovresti avere qualcosa del genere:
var arrayNomi: [String] = ["giuseppe", "matteo", "luca", "filippo"]
for _ in arrayNomi {
}
Cosa dobbiamo mettere all’interno delle parentesi graffe?
Dentro lo scope del ciclo for va inserito il codice che vuoi ripetere. Per il momento, aggiungi un print con una stringa di default:
var arrayNomi: [String] = ["giuseppe", "matteo", "luca", "filippo"]
for _ in arrayNomi {
print("Ciao")
}
Come si legge tutto questo codice che abbiamo scritto?
La parola for significa ripeti. Ripeti quante volte? Ripeti per N volte quanti sono gli elementi all’interno “in” della collezione definita dopo la keyword in.
Che cosa deve ripetere?
Il blocco di istruzioni messo all’interno delle parentesi graffe.
Quindi, se ti chiedo quante volte troveremo in console la scritta “Ciao”, cosa mi rispondi? 😂
// Ciao
// Ciao
// Ciao
// Ciao
Attenzione al significato di ciclo o iterazione
Il ciclo non viene eseguito tutto in una volta. Come ti ho spiegato in una delle prime lezioni del corso, devi imparare a leggere il codice come se fosse un romanzo.
Il nostro compilatore parte leggendo la keyword for, quindi capisce che deve eseguire delle iterazioni. Legge in e sa che sta camminando all’interno di un array.
Immagina il ciclo come una persona che sta camminando in una via in cui ci sono delle case e da ogni casa ci viene restituito un oggetto, immaginiamo una torta.
Ci sono X case per quanti sono gli elementi degli array. Le case sono ordinate per come si trovano gli elementi della collezione.
Il ciclo for-in passa dalla prima casa, entra e prende il suo valore (la torta), poi torna in macchina e la mangia, cioè esegue le istruzioni all’interno del blocco di codice definito dalle parentesi graffe.
Finito di mangiare la torta, si guarda avanti e vede che ci sono altre case. Quindi, passa alla successiva, prende la torta e la mangia.
Questo significa che, anche se non lo notiamo, passa un po’ di tempo tra un’iterazione e l’altra e sopratutto, lo stato del ciclo, cioè la torta anche se sembra uguale (nel nostro caso stampiamo sempre “Ciao”) in realtà sono entità differenti.
Intercettare i valori iterati
Ti ho detto che il ciclo for in legge e prende i valori dall’array iterato. Questo significa che, ad ogni esecuzione, possiamo utilizzare questo valore intercettato all’interno del blocco di parentesi graffe.
Il valore iterato viene conservato all’interno di una costante e, se vogliamo utilizzarla, dobbiamo specificare l’etichetta (quindi che nome assegnargli) al posto del simbolo underscore:
var arrayNomi: [String] = ["giuseppe", "matteo", "luca", "filippo"]
for nome in arrayNomi {
print("Ciao", nome)
}
// Ciao giuseppe
// Ciao matteo
// Ciao luca
// Ciao Filippo
Questa costante, che nell’esempio ho chiamato nome, ad ogni iterazione passa al valore successivo all’interno dell’array.
Comincia a piacerti questo linguaggio Swift? fammelo sapere nei commenti 😂
Posizione dell’iteratore all’interno dell’array
Uno degli esercizi più classici per capire il funzionamento, del ciclo for-in del linguaggio Swift, è quello di provare a stampare la posizione della costante iterativa.
Come possiamo fare?
Potremmo utilizzare una variabile Int definita prima che parta il ciclo e poi incrementarla durante l’esecuzione del ciclo.
var arrayNomi: [String] = ["giuseppe", "matteo", "luca", "filippo"]
var index: Int = 0
for nome in arrayNomi {
print("index: \(index) - value: \(nome)")
index += 1
}
// index: 0 - value: giuseppe
// index: 1 - value: matteo
// index: 2 - value: luca
// index: 3 - value: filippo
Quindi, prima che finisca il ciclo stiamo incrementando la variabile index di 1 (ti ricordo che quella sintassi è equivalente a index = index + 1) e, di conseguenza, al ciclo successivo la variabile sarà uguale al valore precedentemente calcolato.
In questo modo potremmo anche far a meno di utilizzare la variabile iteratrice e potremmo sfruttare solamente l’index per leggere i valori dell’array:
var index: Int = 0
for _ in arrayNomi {
print("Ciao", arrayNome[index])
index += 1
}
Esercizio 1. Cosa succede se la var index invece di partire dal valore 0 parte da 1?
Esercizio 2. Perché ho definito la variabile all’esterno del ciclo e non all’interno?
Prova a spostare l’istruzione var index: Int = 0 all’interno del ciclo e prova a capire cosa succede.
Se non riesci a capire il perché scrivimi pure un commento in fondo alla lezione.
Ciclo for-in e dictionary
Dato che abbiamo studiato, insieme agli array, anche i Dictionary è utilissimo vedere come possiamo sfruttare il ciclo for-in per leggere i suoi valori.
Immaginiamo d’avere un dizionario che rappresenta i salari del personale di un’azienda:
let salaries: [String : Int] = [
"Giuseppe Sapienza" : 3000,
"Luca Pappalardo" : 7000,
"Matteo Laudani" : 5000
]
Il type Tuple
Prima di buttarci dentro il ciclo for, devo fare una piccola digressione per parlarti di un altro tipo di dato, del linguaggio Swift, che non ho avuto modo di mostrarti durante la lezione dei Type.
Sto parlando del type Tuple.
Una tupla è una lista di valori separati da virgola e racchiusi da parentesi tonde. Sembra simile all’array ma il suo utilizzo è differente. Vediamo la sintassi:
let tuple: (Int, String) = (3000, "Giuseppe Sapienza")
Per leggere i valori della tupla, puoi accedere alle sue proprietà che vengono numerate da zero fino X dove X è l’indice dell’ultimo elemento:
let tuple: (Int, String) = (3000, "Giuseppe Sapienza")
tuple.0 // accedo a 3000
tuple.1 // accedo a Giuseppe Sapienza
Etichette
Dato che è facile confondersi con le Tuple ed i suoi valori, possiamo associare ad ogni posizione un’etichetta. L’etichetta o label va specifica come se fosse una variabile all’interno della tupla stessa. Quindi:
let tuple: (id: Int, name: String) = (3000, "Giuseppe Sapienza")
tuple.id // accedo a 3000
tuple.name // accedo a Giuseppe Sapienza
In questo modo è decisamente più semplice accedere ai valori della tupla.
Le tuple, a differenza degli array, vanno utilizzate per piccole quantità di informazioni che sono accumunate da una logica e che non necessitano della complessità di un array.
Immaginiamo per esempio alle coordinate di un point sullo schermo. Non avrebbe senso utilizzare un array per specificare solamente due valori. Basta creare una tupla e definire le sue label:
let point: (x: Double, y: Double) = (0.5, 90)
print("coordinata x:", point.x)
print("coordinata y:", point.y)
Tuple, for-in e dictionary
Adesso che abbiamo questo nuovo strumento possiamo vedere come utilizzare il ciclo for in per leggere un dizionario.
La sintassi base è la stessa, solamente che cambia il type inferred della costante utilizzata come iteratore. Nel caso dei dizionari, la costante, è esattamente una tupla:
let salaries: [String : Int] = [
"Giuseppe Sapienza" : 3000,
"Luca Pappalardo" : 7000,
"Matteo Laudani" : 5000
]
for user in salaries {
print(user.key) // key del dict
print(user.value) // value associata alla key
}
All’interno dello scope potrai accedere alle proprietà:
- .key che rappresenta una delle chiavi del dizionario
- .value che è il valore associato a quella key.
Questo significa che, all’interno di un ciclo, i valori del dizionario vengono trattati come tuple dove ogni valore è una coppia definita dalla key e dal value associato. Quindi un potremmo considerarlo come un ciclo che itera all’interno di un array di tuple.
Un altro sistema per leggere il dizionario è quello di specificare l’iteratore come se fosse una tupla invece che una singola costante, quindi potrai fare:
for (key, value) in salaries {
print(key, value)
}
P.S. Ricordati che le etichette dell’iteratore le puoi definire come preferisci. Il consiglio è quello di usare nomi in linea con il contenuto della collezione letta. Importante: dato che l’iteratore conterrà un solo valore della collezione utilizza il singolare (anche nel caso di dizionari conterrà una coppia per ciclo).
Ciclo e dictionary ordinato
Una domanda che sempre mi fate arrivate a questo punto è:
Perché il dizionario viene stampato in un ordine casuale?
I dizionari vengono gestiti in una maniera differente rispetto agli array. Per massimizzarne la loro efficienza, quando si prova ad accedere utilizzando un ciclo, il sistema lo ordina in maniera casuale. In pratica, ogni volta che eseguirai un ciclo, otterrai ordinamenti differenti.
Come posso iterare il dizionario mantenendo l’ordine definito da noi?
Puoi utilizzare la proprietà keys del dictionary che ti restituisce un array di chiavi. Se utilizzi il ciclo for-in su questa proprietà, l’iteratore conterrà la chiave e potrai usare questa key per accedere ai valori del dizionario:
let salaries: [String : Int] = [
"Giuseppe Sapienza" : 3000,
"Luca Pappalardo" : 7000,
"Matteo Laudani" : 5000
]
for key in salaries.keys {
let salary = salaries[key]
print(key, salary)
}
Se non dovessi ricordarti il significato della sintassi salaries[key] l’ho spiegata all’interno della lezione dei dizionari.
Break: Il primo control flow
Esistono delle particolari keywords che ti permetteranno di alterare il normale flusso di esecuzione del codice. Queste keywords si chiamano control flow o istruzioni di trasferimento.
La prima che ti mostrerò si chiama break e la sua peculiarità è quella di bloccare l’esecuzione di un ciclo (si usa anche in un altro contesto, ma noi non lo tratteremo in questa lezione).
let messages = ["ciao", "come stai?", "hai letto questo messaggio?", "oggi parliamo di ciclo for in Swift"]
for message in messages {
print(message)
break
}
// "ciao"
Perché è stato stampato solo il primo valore?
Come ti dicevo, l’istruzione break, una volta eseguita permette di uscire immediatamente dal ciclo.
Perché è utilissimo il break?
In alcune situazioni può letteralmente far risparmiare un sacco di tempo ed un sacco di risorse alla CPU. Come?
Immagina di voler cercare all’interno di un array un valore e di voler uscire dal ciclo subito dopo averlo trovato. Esempio, ho una lista di contatti unici (nickname o id, pensa ad Instagram) e voglio cercarne uno e non appena lo trovo non ha più senso scorrere l’array:
let followers: [String] = ["xcoding.it", "01ddd.user", "aaa35.surname", "peppe.sapienza", "user.nick1", "aaaa.244"]
let query: String = "peppe.sapienza"
var index: Int = 0
for user in followers {
print("index: \(index)")
if (user == query) {
print("utente trovato: \(user)")
break
}
index += 1
}
Grazie al break gli ultimi due elementi non sono stati iterati e questo significa aver risparmiato due cicli di iterazione. Può sembrare banale ma, in liste con milioni di elementi, utilizzare questo approccio significa cambiare totalmente le prestazioni di un progetto.
Quindi, anche se il linguaggio Swift è super ottimizzato, presta sempre attenzione! 🤓
Loop e Range definiti
Abbiamo visto che i cicli o loop sono utilissimi per iterare all’interno di collezioni come array e dizionari. Però non vengono sempre usati così, ci sono casi in cui ci serve eseguire delle operazioni in maniera iterativa senza dover dipendere da un array.
Qui entra in gioco il type Range.
Un range è un intervallo di valori definito da un minimo, un massimo ed una sequenza che ti permette di andare da un estremo all’altro.
Più facile a farsi che a dirsi.
Se volessimo, per esempio, definire l’intervallo di numeri da 1 a 10, allora scriveremo così:
let range = 1...10
Questo type Range può ovviamente essere utilizzarlo all’interno di un ciclo for-in per eseguire un numero di azioni ben definito ed indipendente da una collezione.
for _ in 1...5 {
print("ciao")
}
// possiamo comunque intercettare i valori del range utilizzando un iteratore
for i in 4...12 {
print("numero:", i)
}
Esiste anche un’altra sintassi per utilizzare un Range che è quella ci permette di escludere l’estremo superiore:
for i in 0..<4 {
print("i", i)
}
Qual è il tipo di dato del Range?
È abbastanza complicato da spiegare adesso, ma se lo volessi esplicitare dovrei scrivere:
let range: ClosedRange<Int> = 1...10
Questo type rientra nella famiglia dei Generics ed in questo caso puoi leggere il type così: ClosedRange generico di tipo Int.
Nel caso invece del range aperto, quindi che esclude l’estremo, devi utilizzare il generic type Range:
let range: Range<Int> = 0..<4
Questi particolari tipo di dato sono una delle armi più potenti che il linguaggio Swift ti mette a disposizione. Noi non li investigheremo ora perché fanno parte di uno set di strumenti avanzati che si studia quando già si ha un po’ di dimestichezza con la materia.
Però se vuoi dargli un’occhiata o vuoi salvarti la lezione per il futuro, qui ho scritto un articolo che parla nel dettaglio dei Generics con il linguaggio Swift.
Conclusione
Devo essere sincero, non ti ho mostrato la vera istruzione for. O meglio, non ti ho spiegato il for per come l’ho imparato io (in C, Java e compagnia).
Infatti io ti ho parlato dell’istruzione for-in che è una interpretazione moderna del for che è presente nel linguaggio Swift ed in altri linguaggi moderni.
Serve imparare l’altra?
A mio modesto parere no. Da quando è nato Swift ed ha quando ho imparato il for-in non ho mai sentito il bisogno di utilizzare la vecchia sintassi (ed a dirla tutta non ricordo nemmeno più se sia possibile utilizzarla ancora 😂).
Ad ogni modo, il ciclo for-in del linguaggio Swift è una manna dal cielo e ti risolverà tantissimi problemi.
E a proposito di problemi, ti lascio qui un po’ d’esercizi da risolvere 🤓.
Esercizi della lezione
- Esercizio 3. Se “var punteggi = [ 20, 14, 55, 5, 7, 8, 36]” è un array e “var punteggioFinale” è una variabile, implementa un ciclo che sommi tutti i valori dell’array e salvi il risultato nella variabile punteggioFinale.
- Esercizio 4. Crea un’istruzione di controllo che, date due variabili intere, stampi su schermo “a è maggiore di b” oppure il contrario.
- Esercizio 5. Creare due array vuoti di interi e inizializzare il primo con valori a scelta. Copiate gli elementi del primo array tramite un ciclo nel secondo array.
- Esercizio 6. Crea un ciclo for che, dato un’array di interi, stampa solo i numeri maggiori di 10 dell’array.
Buona Programmazione!