Lezione 3 di 7 · 16 min di lettura
Ripetere con while e do
Il primo ciclo. Ripetere finché una condizione è vera, fermarsi con break, saltare un giro con continue, e come uscire da un ciclo infinito.
Il superpotere della ripetizione
Un computer non è più intelligente di te, ma ha una qualità che nessun umano ha: non si stanca mai. Fargli ripetere un’operazione un milione di volte costa pochi millisecondi. Tutto quello che serve è dirgli cosa ripetere e fino a quando.
Il ciclo più semplice è while (“finché”):
import std::io;
fn void main()
{
int seconds = 5;
while (seconds > 0)
{
io::printfn("%d...", seconds);
seconds--;
}
io::printn("Liftoff!");
}5...
4...
3...
2...
1...
Liftoff!Assomiglia a un if, con una differenza fondamentale: arrivato alla graffa chiusa, il programma torna su e ricontrolla la condizione. Finché è vera, esegue il blocco di nuovo. Quando diventa falsa, prosegue dopo il ciclo.
Ogni ripetizione del blocco si chiama iterazione (o, più alla buona, “giro”). Qui ce ne sono cinque: seconds parte da 5 e a ogni giro seconds-- lo abbassa di uno. Al sesto controllo vale 0, 0 > 0 è falso, e il ciclo finisce.
Dettagli nerd Un ciclo, per il processore, è un salto all'indietro
Nella lezione sull’if abbiamo visto che il processore sa saltare a un’altra istruzione. Un if salta in avanti, per scavalcare un blocco. Un while aggiunge un salto all’indietro: in fondo al blocco c’è un “torna al controllo della condizione”. Tutto qui: ogni ciclo, in qualsiasi linguaggio, si riduce a un confronto e a due salti.
Il ciclo infinito (e come uscirne)
Cosa succede se dimentichi di far cambiare la variabile?
int i = 0;
while (i < 3)
{
io::printn(i); // i is never incremented...
}i resta 0 per sempre, la condizione resta vera per sempre, e il programma stampa 0 all’infinito. È un ciclo infinito, e prima o poi lo scriverai anche tu (lo fanno tutti). Niente panico: nel terminale premi Ctrl+C e il programma si ferma.
Dettagli nerd Cosa fa davvero Ctrl+C?
Quando premi Ctrl+C, il terminale non “uccide” il programma direttamente: chiede al sistema operativo di mandargli un segnale, una specie di messaggio urgente. Quello di Ctrl+C si chiama SIGINT (interrupt). Un programma può decidere di intercettarlo, per esempio per salvare il lavoro prima di chiudersi; se non lo fa, come i nostri, il sistema operativo lo termina.
Il ciclo infinito, però, non è sempre un errore. A volte è proprio quello che vuoi: un videogioco ripete “leggi i tasti, aggiorna, disegna” finché il giocatore non esce. Si scrive while (true), e si esce da dentro con break, che vediamo tra un attimo.
do: prima fai, poi controlla
Il while controlla la condizione prima di ogni giro. Se è falsa fin dall’inizio, il blocco non viene eseguito nemmeno una volta. A volte invece serve il contrario: fare il lavoro almeno una volta, e poi decidere se ripetere. Per questo c’è do ... while:
int k = 10;
while (k < 5)
{
io::printfn("while: k = %d", k);
k++;
}
do
{
io::printfn("do: k = %d", k);
k++;
} while (k < 5);do: k = 10La condizione k < 5 è falsa fin da subito. Il while non entra mai; il do esegue il blocco una volta e solo dopo controlla, trovando la condizione falsa. Nota il punto e virgola dopo while (k < 5): nel do ci vuole.
Il caso tipico è chiedere qualcosa all’utente: la domanda va fatta almeno una volta, e ripetuta finché la risposta non è valida. Lo faremo quando impareremo a leggere dalla tastiera.
Quiz
Con int x = 5;, quante volte viene stampato hi da while (x < 3) { io::printn("hi"); x++; } e quante da do { io::printn("hi"); x++; } while (x < 3);?
break e continue
Dentro un ciclo hai due comandi per cambiare il percorso:
breakesce subito dal ciclo, qualunque cosa dica la condizione.continuesalta il resto del giro corrente e passa al prossimo controllo della condizione.
Con break il while (true) diventa utile: “cerca finché non trovi”.
int n = 51;
while (true)
{
if (n % 7 == 0) break;
n++;
}
io::printfn("First multiple of 7 after 50: %d", n);First multiple of 7 after 50: 56E con continue puoi scartare alcuni giri. Qui stampiamo i numeri da 1 a 10 saltando i multipli di 3:
int i = 0;
while (i < 10)
{
i++;
if (i % 3 == 0) continue;
io::printf("%d ", i);
}
io::printn();1 2 4 5 7 8 10Attenzione all’ordine: i++ sta prima del continue. Se fosse dopo, al primo multiplo di 3 il continue salterebbe l’incremento, i resterebbe 3 per sempre… e avresti un ciclo infinito. (io::printn() senza argomenti, alla fine, va solo a capo.)
Un classico: smontare un numero cifra per cifra
Il while brilla quando non sai in anticipo quanti giri serviranno. Per esempio: quante cifre ha un numero, e quanto fa la loro somma?
import std::io;
fn void main()
{
int number = 2026;
int sum = 0;
int digits = 0;
while (number > 0)
{
sum += number % 10; // take the last digit
number /= 10; // and drop it
digits++;
}
io::printfn("%d digits, sum %d", digits, sum);
}4 digits, sum 10Il trucco usa i due operatori della trappola del modulo 1, che qui diventano alleati: % 10 dà l’ultima cifra (2026 → 6), / 10 tra interi la toglie (2026 → 202). Si ripete finché resta qualcosa: 6, poi 2, poi 0, poi 2. Somma 10, quattro giri.
Esercizio · sul tuo computer
La congettura di Collatz
Prendi un numero intero positivo. Se è pari, dividilo per 2; se è dispari, moltiplicalo per 3 e aggiungi 1. Ripeti. La congettura di Collatz dice che, qualunque numero tu scelga, prima o poi arriverai a 1. Nessuno è mai riuscito a dimostrarlo, ma nessuno ha mai trovato un numero per cui non funzioni.
Scrivi collatz.c3 partendo da int n = 6;:
- finché
nnon vale 1, stampanseguito da uno spazio (conio::printf), poi applica la regola; - conta i passi in una variabile
steps; - alla fine stampa l’1 finale con
io::printn(n)e poi la rigaSteps:seguita dal numero di passi.
Riporta l’output. Poi prova con int n = 27;: sembra innocuo, ma servono 111 passi e si arriva fino a 9232.
Mostra una soluzione (prima prova da solo!)
import std::io;
fn void main()
{
int n = 6;
int steps = 0;
while (n != 1)
{
io::printf("%d ", n);
if (n % 2 == 0)
{
n /= 2;
}
else
{
n = n * 3 + 1;
}
steps++;
}
io::printn(n);
io::printfn("Steps: %d", steps);
}Ricapitolando
while (condizione) { ... }ripete il blocco finché la condizione è vera; controlla prima di ogni giro.do { ... } while (condizione);esegue almeno una volta e controlla dopo.breakesce dal ciclo,continuesalta al giro successivo.while (true)più unbreakè il modo di dire “ripeti finché non succede qualcosa”.- Un ciclo infinito si ferma con Ctrl+C. Per evitarlo, qualcosa deve cambiare a ogni giro.
Il while è perfetto quando non sai quanti giri serviranno. Quando invece lo sai (“fallo 10 volte”, “per ogni numero da 1 a 100”) c’è uno strumento più compatto: il for.