30 aprile 2022

Cicli: while e for

Abbiamo spesso bisogno di eseguire la stessa azione più volte di fila.

Ad esempio, quando abbiamo bisogno di ritornare, una dopo l’altra, della merce da una lista; o anche solo eseguire lo stesso codice per ogni numero da 1 a 10.

I cicli sono un modo di ripetere una stessa parte di codice più volte.

Il ciclo “while”

Il ciclo while ha la seguente sintassi:

while (condition) {
  // codice
  // "corpo del ciclo"
}

Fino a che la condition è true, il code nel corpo del ciclo viene eseguito.

Ad esempio, il ciclo qui sotto mostra i fino a che i < 3:

let i = 0;
while (i < 3) { // mostra 0, poi 1, poi 2
  alert( i );
  i++;
}

Un’esecuzione del codice nel corpo del ciclo viene chiamata un’iterazione. Il ciclo nell’esempio sopra fa tre iterazioni.

Se nell’esempio sopra non ci fosse i++, il ciclo si ripeterebbe per sempre; in teoria: in pratica, il browser ha dei metodi per bloccare questi cicli. In JavaScript server-side è necessario arrestare il processo.

Qualsiasi espressione o variabile può essere utilizzata come condizione di un ciclo, non solo un confronto come i < 3. Il ciclo while converte le espressioni al tipo booleano, che vengono poi valutate.

Ad esempio, un modo più breve di scrivere while (i != 0) potrebbe essere while (i):

let i = 3;
while (i) { // quando i diventa 0, la condizione diventa falsa e il ciclo si conclude
  alert( i );
  i--;
}
Le parentesi non sono richieste per un corpo composto da una singola linea di codice

Se il corpo del ciclo ha una singola istruzione, possiamo omettere le parentesi {…}:

let i = 3;
while (i) alert(i--);

Il ciclo “do…while”

La condizione da controllare può essere messa dopo il corpo del ciclo. Lo si fa utilizzando la sintassi do..while:

do {
  // corpo del ciclo
} while (condition);

Il ciclo esegue prima il corpo, poi controlla la condizione; se questa è vera, esegue nuovamente il corpo.

Ad esempio:

let i = 0;
do {
  alert( i );
  i++;
} while (i < 3);

Questa tipo di sintassi viene usata molto raramente, ad eccezione dei casi in cui si vuole che il corpo del ciclo venga eseguito almeno una volta. Questo avviene ancor prima del controllo della condizione. La forma più comune, comunque, è while(…) {…}.

Il ciclo “for”

Il ciclo for è forse il più utilizzato.

La sua sintassi è:

for (begin; condition; step) {
  // ... corpo del ciclo ...
}

Cerchiamo ora di capire, tramite esempi, il suo funzionamento. Il ciclo sotto esegue alert(i) fino a quando la variabile i è più piccola di 3. A ogni iterazione, il valore di i aumenta di 1.

for (let i = 0; i < 3; i++) { // mostra 0, poi 1, poi 2
  alert(i);
}

Esaminiamo l’istruzione for parte per parte:

Parte
begin i = 0 Viene eseguito una volta sola, all’entrata nel ciclo.
condition i < 3 Viene controllata prima di ogni iterazione; se falsa, il ciclo si interrompe.
body alert(i) Viene eseguito fino a quando la condizione è vera.
step i++ Viene eseguito ad ogni iterazione, dopo il corpo, fintato che la condizione è true.

L’iterazione, generalmente, funziona nel modo seguente:

Eseguiamo begin
→ (if condition → run body and run step)
→ (if condition → run body and run step)
→ (if condition → run body and run step)
→ ...

Ricapitolando: begin viene eseguito per primo, una volta sola; subito dopo, inizia l’iterazione. Se condition, dopo la conversione a booleano, è true, vengono eseguiti body e step; se è false, il ciclo si interrompe.

Se i cicli vi sono nuovi, forse vi sarà d’aiuto tornare indietro agli esempi e provare a riprodurli, passo passo, su un foglio di carta.

Ecco esattamente, in dettaglio, ciò che avviene nel nostro codice:

// for (let i = 0; i < 3; i++) alert(i)

// inizia l'esecuzione, viene dichiarata la variabile i
let i = 0
// if(condition == true) → esegue il corpo e avanza
if (i < 3) { alert(i); i++ }
// if(condition == true) → esegue il corpo e avanza
if (i < 3) { alert(i); i++ }
// if(condition == true) → esegue il corpo e avanza
if (i < 3) { alert(i); i++ }
// ...si conclude, perché ora i == 3 e la condizione i < 3 == false
Dichiarazioni di variabili inline

Qui il “counter”, che utilizzeremo nella nostra condition, è una variabile: i. Viene dichiarata all’interno del corpo del ciclo ed è accessibile solo al suo interno. Questo tipo di espressione si chiama “dichiarazione di una variabile inline”.

for (let i = 0; i < 3; i++) { //'i' è definito, e accessibile, solo dentro il corpo del ciclo
  alert(i); // 0, 1, 2
}
alert(i); // errore, nessuna variabile 'i' fuori dal corpo del ciclo

Invece di definire una nuova variabile, possiamo utilizzarne una già esistente:

let i = 0;

for (i = 0; i < 3; i++) { // utilizza una variabile esistente
  alert(i); // 0, 1, 2
}

alert(i); // 3; la variabile `i` è accessibile (è stata dichiarata fuori dal corpo del ciclo)

Parti opzionali

Ogni parte del ciclo for è opzionale.

Ad esempio, possiamo omettere begin se non abbiamo bisogno di una variabile per la nostra condition.

Come in questo esempio:

let i = 0; // la variabile 'i' è stata già dichiarata e assegnata

for (; i < 3; i++) { // saltiamo "begin"
  alert( i ); // 0, 1, 2
}

Possiamo anche rimuovere lostep:

let i = 0;

for (; i < 3;) {
  alert( i++ ); //la variabile 'i' viene incrementata nel corpo del ciclo
}

Il ciclo, nell’esempio sopra, diventa uguale ad un while (i < 3).

Possiamo rimuovere tutto, ma questo risulterebbe in un ciclo infinito:

for (;;) {
  // esegue il corpo del ciclo senza mai terminare
}

Nota che le due ; del ciclo for devono essere presenti, altrimenti vi sarebbe un errore di sintassi.

Interrompere un ciclo

Normalmente un ciclo termina quando la condizione diventa falsa.

Ma è possibile forzare l’uscita in qualsiasi momento. C’è una speciale direttiva per fare questo: break.

Ad esempio, il ciclo sotto richiede all’utente una serie di numeri, e termina quando nessun numero viene inserito:

let sum = 0;

while (true) {

  let value = +prompt("Enter a number", '');

  if (!value) break; // (*)

  sum += value;

}
alert( 'Sum: ' + sum );

La direttiva break viene attivata alla linea (*), quando l’utente inserisce una linea vuota o annulla la procedura di input. Questo ferma il ciclo immediatamente, passando il controllo alla prima linea successiva al ciclo. In questo caso, alert.

La combinazione “ciclo infinito + break quando necessario” è utile in situazioni in cui la condizione deve essere verificata in un punto differente dall’inizio/fine del ciclo (questo può avvenire in un qualsiasi altro punto del corpo).

Vai alla prossima iterazione

La direttiva continue è una versione leggera del break. Non blocca l’intero ciclo: interrompe solo l’iterazione corrente e forza il ciclo a passare all’iterazione successiva.

Possiamo utilizzarla se abbiamo finito con le operazioni che ci interessano e vogliamo passare all’iterazione seguente.

Il ciclo sotto usa continue per ritornare solo i valori dispari:

for (let i = 0; i < 10; i++) {

  // se condizione == true (`i` è pari) salta la restante parte di codice a passa alla prossima iterazione
  if (i % 2 == 0) continue;
  //se `i` è dispari, esegui codice
  alert(i); // 1, poi 3, 5, 7, 9 (nota le due condizioni sopra: abbiamo solo i con un valore dispari, e inferiore a 10)
}

Per i valori pari di i la direttiva continue interrompe l’esecuzione del corpo e passa il controllo alla successiva iterazione del for (i viene incrementato, poi si controlla che la condizione sia true). Di conseguenza, l’alert viene seguito solo con i valori dispari.