1. Introducción
Vamos a empezar con una pregunta: ¿para qué demonios meter un bucle dentro de otro? Pues resulta que muchas veces nuestros datos o tareas no están organizados en una sola línea, sino, por ejemplo, en una tabla, una cuadrícula o incluso en una estructura multidimensional. Supón que quieres mostrar en pantalla la tabla de multiplicar, recorrer un array bidimensional o contar las intersecciones entre todos los pares de elementos. Aquí un solo bucle no te va a servir — necesitas un bucle dentro de otro.
En programación, un bucle anidado es como tener dos alarmas: la de fuera empieza a sonar, y dentro de ella se lanza otra, que va a sonar cada vez mientras la primera esté activa. Así, mientras va una iteración "externa", la interna recorre todo su camino (y lo hace una y otra vez para cada iteración de la externa).
Un buen ejemplo: horas y minutos. Las horas son el bucle externo de 0 a 23, los minutos son el bucle interno de 0 a 59. Por cada cambio en el bucle externo, el interno recorre todos sus valores.
2. Sintaxis de bucles anidados
En C# la sintaxis de los bucles anidados no es diferente de los bucles normales — simplemente escribes un bucle dentro del cuerpo de otro. Mira los ejemplos con for y while:
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
{
Console.Write($"{i},{j} ");
}
Console.WriteLine();
}
Aquí el bucle externo controla la variable i (de 0 a 2), y el interno — la variable j (de 0 a 3). Para cada valor de i el bucle interno recorre completamente desde j=0 hasta j=3. Si ejecutas este código, vas a ver una tabla de coordenadas muy maja:
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 ejemplo parecido usando while:
int i = 0;
while (i < 3)
{
int j = 0;
while (j < 4)
{
Console.Write($"{i},{j} ");
j++;
}
Console.WriteLine();
i++;
}
Ojo: dentro de cada pasada del bucle externo la variable del bucle interno (j) tiene que inicializarse de nuevo, ¡si no solo verás una línea!
3. Ejemplos de uso de bucles anidados
Ejemplo 1: mostrar un tablero de ajedrez (8x8)
Nuestra primera tarea: mostrar en pantalla un tablero de ajedrez clásico con casillas negras y blancas (que # sea negra, _ sea blanca). Lo hacemos con bucles anidados 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();
}
Resultado:
_#_#_#_#
#_#_#_#_
_#_#_#_#
#_#_#_#_
_#_#_#_#
#_#_#_#_
_#_#_#_#
#_#_#_#_
Punto importante: la anidación asegura que para cada fila (row) recorremos todas las columnas (col). Sin anidación no tendríamos la estructura del tablero — solo una fila o una columna.
Ejemplo 2: tabla de multiplicar
¡Uno de los clásicos para bucles anidados! Vamos a mostrar la tabla de multiplicar del 1 al 9:
for (int i = 1; i <= 9; i++)
{
for (int j = 1; j <= 9; j++)
{
Console.Write($"{i * j,3} ");
}
Console.WriteLine();
}
El formateo ${i * j,3} mete espacios para que la tabla quede bien alineada.
Resultado:
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. Bucles anidados y su control — matices
Sobre el efecto de break y continue en bucles anidados
¡Aquí es donde muchos novatos se la pegan! Si usas break o continue en el bucle interno, afectan solo a ese bucle. El externo sigue a lo suyo como si nada.
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
if (j == 3)
break; // ¡salimos solo del bucle interno!
Console.Write($"{i},{j} ");
}
Console.WriteLine();
}
Resultado:
0,0 0,1 0,2
1,0 1,1 1,2
2,0 2,1 2,2
Si necesitas salir de dos bucles anidados a la vez (por ejemplo, terminar la búsqueda al primer match en una tabla), normalmente se usa una bandera o algún truco especial (por ejemplo, return — si todo está dentro de una función).
5. Visualización de bucles anidados
A veces cuesta "ver" la secuencia de ejecución de los bucles anidados. Mira este diagrama de bloques:
En forma de tabla — ¿cuántas iteraciones habrá si i va de 1 a 3, y j de 1 a 4?
| i | j (recorrido para cada i) | Iteraciones del bucle interno |
|---|---|---|
| 1 | 1, 2, 3, 4 | 4 |
| 2 | 1, 2, 3, 4 | 4 |
| 3 | 1, 2, 3, 4 | 4 |
| Total: 3 × 4 = 12 |
6. Errores y trampas al trabajar con bucles anidados
Error típico — inicializar mal la variable del bucle interno. Por ejemplo, declararla fuera del bucle externo, pero no reiniciarla en cada paso. Al final el bucle interno puede no ejecutarse nunca, o hacerlo mal.
int j = 0;
for (int i = 0; i < 3; i++)
{
while (j < 4) // ¡Ups! j ya puede ser igual a 4 después de la primera vuelta.
{
Console.Write($"{i},{j} ");
j++;
}
Console.WriteLine();
}
Aquí el bucle solo se ejecuta una vez. No olvides inicializar las variables de los bucles internos dentro de los externos.
Además, si por error escribes dos bucles anidados con las mismas variables, el compilador te va a regañar: la variable ya está definida.
for (int i = 0; i < 3; i++)
{
for (int i = 0; i < 3; i++)
{
Console.Write($"{i},{i} ");
}
Console.WriteLine();
}
GO TO FULL VERSION