Lezione 4 di 7 · 17 min di lettura
Il ciclo for
Contare in modo compatto. Le tre parti del for, i passi diversi da uno, i cicli annidati e come uscire da due cicli insieme.
Tre ingredienti, una riga
Guarda un tipico ciclo while che conta da 1 a 5:
int i = 1; // 1. where to start
while (i <= 5) // 2. when to stop
{
io::printf("%d ", i);
i++; // 3. what changes each round
}Sono le tre domande della lezione scorsa, sparse su quattro righe diverse. Il ciclo for le mette tutte in fila, tra le stesse parentesi:
for (int i = 1; i <= 5; i++)
{
io::printf("%d ", i);
}
io::printn();1 2 3 4 5Le tre parti sono separate da punti e virgola:
| Parte | Esempio | Quando viene eseguita |
|---|---|---|
| Inizializzazione | int i = 1 | Una volta sola, prima di tutto |
| Condizione | i <= 5 | Prima di ogni giro: se è falsa, il ciclo finisce |
| Aggiornamento | i++ | Alla fine di ogni giro |
Il comportamento è identico al while di sopra, ma chi legge capisce tutto il ciclo guardando una riga sola. Quando sai quante volte ripetere, o stai contando, il for è la scelta naturale. Quando ripeti “finché succede qualcosa”, resta il while.
Contare in tutti i modi
Nessuno ti obbliga a partire da 0 o da 1, né ad andare avanti di uno:
for (int i = 10; i > 0; i -= 3) io::printf("%d ", i); // backwards, by 3
io::printn();
for (int i = 0; i <= 10; i += 2) io::printf("%d ", i); // even numbers
io::printn();10 7 4 1
0 2 4 6 8 10(Anche il for, come l’if, accetta una sola istruzione sulla stessa riga senza graffe.)
Quiz
Quante volte gira for (int i = 0; i < 10; i += 3) { ... }?
La variabile del for vive solo lì
La i dichiarata nel for appartiene al ciclo: esiste durante i giri e sparisce appena il ciclo finisce.
for (int i = 0; i < 3; i++) {}
io::printn(i); // error: 'i' could not be found, did you spell it right?È una buona notizia: puoi riusare i nel ciclo successivo senza conflitti, e nessuno può usare per sbaglio il valore rimasto dopo il ciclo. Se quel valore ti serve davvero, dichiara la variabile prima del for.
Accumulare: la somma di Gauss
Uno schema che userai sempre: una variabile accumulatore dichiarata prima del ciclo e aggiornata a ogni giro.
import std::io;
fn void main()
{
int sum = 0;
for (int i = 1; i <= 100; i++)
{
sum += i;
}
io::printfn("1 + 2 + ... + 100 = %d", sum);
}1 + 2 + ... + 100 = 5050Cicli dentro cicli
Il blocco di un for può contenere qualsiasi cosa, compreso un altro for. Per ogni giro del ciclo esterno, quello interno fa tutti i suoi giri. Ecco la tavola pitagorica, 5 per 5:
import std::io;
fn void main()
{
for (int row = 1; row <= 5; row++)
{
for (int col = 1; col <= 5; col++)
{
io::printf("%4d", row * col);
}
io::printn();
}
} 1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
5 10 15 20 25Il ciclo interno scrive una riga intera (cinque numeri, ognuno largo 4 caratteri grazie a %4d), poi io::printn() va a capo, e il ciclo esterno passa alla riga successiva. In tutto, printf viene chiamata 5 × 5 = 25 volte.
Dettagli nerd Quanto costa un ciclo dentro un ciclo?
Con due cicli annidati da n giri ciascuno, il lavoro è n × n. Per n = 5 sono 25 operazioni, niente. Per n = 1000 sono un milione: ancora pochi millisecondi. Per n = 100 000 sono dieci miliardi, e il programma comincia a metterci secondi o minuti. I programmatori dicono che un algoritmo così è “O di n al quadrato”, scritto O(n²): raddoppi i dati, il tempo quadruplica. Tenerlo a mente è la differenza tra un programma che scala e uno che si pianta con dati veri.
Uscire da due cicli insieme
break esce dal ciclo in cui si trova, cioè da quello più interno. Ma se stai cercando qualcosa in una tabella e l’hai trovato, vuoi uscire da entrambi. C3 permette di dare un’etichetta a un ciclo e di dire a break (o a continue) quale ciclo intendi:
import std::io;
fn void main()
{
for OUTER: (int i = 1; i <= 9; i++)
{
for (int j = 1; j <= 9; j++)
{
if (i * j == 42)
{
io::printfn("%d x %d = 42", i, j);
break OUTER;
}
}
}
io::printn("Search over.");
}6 x 7 = 42
Search over.L’etichetta si scrive in MAIUSCOLO, subito dopo for e seguita da due punti. Senza break OUTER, il ciclo esterno continuerebbe e troverebbe anche 7 x 6.
Esercizio · sul tuo computer
Il triangolo
Scrivi triangle.c3 con int height = 4;. Usando due for annidati, disegna un triangolo di asterischi: la riga 1 ha un asterisco, la riga 2 ne ha due, e così via fino a height. Usa io::print("*") per ogni asterisco e io::printn() per andare a capo.
Conta anche gli asterischi stampati in una variabile total, e alla fine stampa Total: seguito dal numero e da stars.
Riporta l’output. Poi prova height = 10: quanti asterischi prevedi, prima di lanciarlo? (Suggerimento: Gauss.)
Mostra una soluzione (prima prova da solo!)
import std::io;
fn void main()
{
int height = 4;
int total = 0;
for (int row = 1; row <= height; row++)
{
for (int star = 1; star <= row; star++)
{
io::print("*");
total++;
}
io::printn();
}
io::printfn("Total: %d stars", total);
}Il trucco è la condizione del ciclo interno, star <= row: il numero di giri interni dipende dalla riga del ciclo esterno.
Ricapitolando
for (inizio; condizione; aggiornamento) { ... }raccoglie un ciclo che conta in una riga sola.- Si può contare all’indietro o a passi diversi da uno:
i -= 3,i += 2. - La variabile dichiarata nel
forvive solo nel ciclo, e se ne può dichiarare una sola. - I cicli annidati moltiplicano i giri:
n × n. break ETICHETTAecontinue ETICHETTAagiscono su un ciclo esterno con nome.
Finora abbiamo contato numeri. Ma spesso si vuole scorrere una collezione di cose: dei voti, delle parole, delle lettere. Per questo C3 ha un ciclo apposito: foreach.