CodeGym /Corsi /C# SELF /Cicli annidati e il loro utilizzo

Cicli annidati e il loro utilizzo

C# SELF
Livello 4 , Lezione 5
Disponibile

1. Introduzione

Partiamo dalla domanda: perché mai dovremmo mettere un ciclo dentro un altro? Il fatto è che spesso i nostri dati o i nostri task non sono organizzati solo in una linea, ma magari in una tabella, una griglia o addirittura in una struttura multidimensionale. Supponiamo che tu voglia stampare la tabellina, scorrere un array bidimensionale o calcolare tutte le intersezioni tra coppie di elementi. Qui un solo ciclo non basta — serve un ciclo dentro un altro ciclo.

Nel mondo della programmazione, un ciclo annidato è come avere due sveglie: quella esterna inizia a suonare, e dentro di lei parte un'altra che suona ogni volta finché la prima è attiva. Quindi, mentre va avanti un'iterazione "esterna", quella interna fa tutto il suo giro (e lo rifà per ogni iterazione dell'esterna).

Un buon esempio sono le ore e i minuti. Le ore sono il ciclo esterno da 0 a 23, i minuti sono il ciclo interno da 0 a 59. Ad ogni cambiamento del ciclo esterno, quello interno riesce a scorrere tutti i suoi valori.

2. Sintassi dei cicli annidati

In C# la sintassi dei cicli annidati non è diversa da quella dei cicli normali — semplicemente scrivi un ciclo dentro il corpo di un altro. Vediamo qualche esempio con for e while:


for (int i = 0; i < 3; i++)
{
    for (int j = 0; j < 4; j++)
    {
        Console.Write($"{i},{j} ");
    }
    Console.WriteLine();
}
Cicli annidati for per stampare le coordinate

Qui il ciclo esterno controlla la variabile i (da 0 a 2), mentre quello interno la variabile j (da 0 a 3). Per ogni valore di i, il ciclo interno va completamente da j=0 a j=3. Se esegui questo codice, vedrai una bella tabella di coordinate:

0,0 0,1 0,2 0,3 
1,0 1,1 1,2 1,3 
2,0 2,1 2,2 2,3 

Un esempio simile usando while:

int i = 0;
while (i < 3)
{
    int j = 0;
    while (j < 4)
    {
        Console.Write($"{i},{j} ");
        j++;
    }
    Console.WriteLine();
    i++;
}

Occhio: dentro ogni giro del ciclo esterno, la variabile del ciclo interno (j) deve essere reinizializzata, altrimenti vedrai solo una riga!

3. Esempi di utilizzo dei cicli annidati

Esempio 1: stampa di una scacchiera (8x8)

Facciamo che il nostro primo task sia stampare una classica scacchiera sullo schermo, usando celle nere e bianche (diciamo che # è nera, _ è bianca). Lo facciamo con i cicli annidati for:


for (int row = 0; row < 8; row++)
{
    for (int col = 0; col < 8; col++)
    {
        if ((row + col) % 2 == 0)
            Console.Write("_");
        else
            Console.Write("#");
    }
    Console.WriteLine();
}
Cicli annidati per stampare la scacchiera

Risultato:

_#_#_#_#
#_#_#_#_
_#_#_#_#
#_#_#_#_
_#_#_#_#
#_#_#_#_
_#_#_#_#
#_#_#_#_

Nota importante: l'annidamento fa sì che per ogni riga (row) passiamo completamente su tutte le colonne (col). Senza annidamento non avremmo la struttura della scacchiera — solo una riga o una colonna.

Esempio 2: tabellina

Uno dei classici per i cicli annidati! Stampiamo la tabellina da 1 a 9:


for (int i = 1; i <= 9; i++)
{
    for (int j = 1; j <= 9; j++)
    {
        Console.Write($"{i * j,3} ");
    }
    Console.WriteLine();
}
Cicli annidati per stampare la tabellina

La formattazione ${i * j,3} aggiunge spazi per rendere la tabella ordinata.

Risultato:

   1   2   3   4   5   6   7   8   9
  2   4   6   8  10  12  14  16  18 
  3   6   9  12  15  18  21  24  27 
  4   8  12  16  20  24  28  32  36 
...
  9  18  27  36  45  54  63  72  81 

4. Cicli annidati e la loro gestione — dettagli

Sull'effetto di break e continue nei cicli annidati

Qui molti principianti ci cascano! Se usi break o continue nel ciclo interno, questi influenzano solo quel ciclo. Quello esterno va avanti come se nulla fosse.


for (int i = 0; i < 3; i++)
{
    for (int j = 0; j < 5; j++)
    {
        if (j == 3)
            break; // usciamo solo dal ciclo interno!
        Console.Write($"{i},{j} ");
    }
    Console.WriteLine();
}
break esce solo dal ciclo interno

Risultato:

0,0 0,1 0,2 
1,0 1,1 1,2 
2,0 2,1 2,2 

Se vuoi uscire subito da entrambi i cicli annidati (tipo, interrompere la ricerca appena trovi la prima corrispondenza in una tabella), di solito si usa un flag o qualche trucco (ad esempio return — se tutto avviene dentro una funzione).

5. Visualizzazione dei cicli annidati

A volte è difficile "vedere" la sequenza di esecuzione dei cicli annidati. Guarda questo diagramma di flusso:

Visualizzazione dei cicli annidati

In forma tabellare — quante iterazioni totali ci saranno per i da 1 a 3, j da 1 a 4?

i j (scorre per ogni i) Iterazioni del ciclo interno
1 1, 2, 3, 4 4
2 1, 2, 3, 4 4
3 1, 2, 3, 4 4
Totale: 3 × 4 = 12

6. Errori e trappole comuni con i cicli annidati

Un errore comune è inizializzare male la variabile del ciclo interno. Ad esempio, dichiararla fuori dal ciclo esterno e non azzerarla a ogni giro. Così il ciclo interno potrebbe non eseguire affatto, o funzionare male.

int j = 0;
for (int i = 0; i < 3; i++)
{
    while (j < 4) // Ops! j potrebbe già essere 4 dopo il primo giro.
    {
        Console.Write($"{i},{j} ");
        j++;
    }
    Console.WriteLine();
}

Qui il ciclo gira solo una volta. Ricordati di inizializzare le variabili dei cicli interni dentro quelli esterni!

Inoltre, se per sbaglio scrivi due cicli annidati con le stesse variabili, il compilatore ti sgrida: la variabile è già definita.


for (int i = 0; i < 3; i++)
{
    for (int i = 0; i < 3; i++)
    {
        Console.Write($"{i},{i} ");
    }
    Console.WriteLine();
}
1
Sondaggio/quiz
Cicli, livello 4, lezione 5
Non disponibile
Cicli
Cicli annidati e il loro utilizzo
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION