Módulo 1 · Lección 19 de 59
Bucles anidados
Un bucle dentro de otro para recorrer filas y columnas, cuántas iteraciones salen en total y qué hace break aquí.
Un bucle recorre una dimensión: los caracteres de un texto, los números de un rango. Para recorrer dos, como las filas y las columnas de una tabla, hace falta un bucle dentro de otro. Cualquier cosa que se organice en cuadrícula, desde una tabla de multiplicar hasta el tablero de un juego, se construye así.
Un bucle dentro de otro#
Dentro del bloque de un for puede ir cualquier instrucción, y un for es una instrucción. El interior se sangra otros cuatro espacios:
for fila in range(1, 4):
for columna in range(1, 4):
print(f"fila {fila}, columna {columna}")fila 1, columna 1
fila 1, columna 2
fila 1, columna 3
fila 2, columna 1
fila 2, columna 2
fila 2, columna 3
fila 3, columna 1
fila 3, columna 2
fila 3, columna 3Lee la salida con atención, porque ahí está toda la lección: el bucle interior se ejecuta entero en cada vuelta del exterior. Mientras fila vale 1, columna recorre 1, 2 y 3 completos. Solo entonces fila pasa a 2, y el interior vuelve a empezar desde 1.
El de fuera avanza despacio; el de dentro, deprisa. Como las agujas de un reloj: la de las horas da una vuelta mientras la de los minutos da doce.
Cuántas iteraciones salen#
Se multiplican. Si el exterior da 3 vueltas y el interior 4, el bloque interior se ejecuta 3 × 4 = 12 veces:
vueltas = 0
for fila in range(3):
for columna in range(4):
vueltas += 1
print(f"El bloque interior se ha ejecutado {vueltas} veces")El bloque interior se ha ejecutado 12 vecesLa tabla de multiplicar#
Es el ejemplo canónico, y aquí sirve además para practicar el formato de columnas de la lección de f-strings. El truco está en que cada fila se va montando en una variable de texto y se imprime de una vez al terminar el bucle interior:
encabezado = f"{'':>4}"
for columna in range(1, 11):
encabezado += f"{columna:>4}"
print(encabezado)
print("-" * 44)
for fila in range(1, 11):
linea = f"{fila:>4}"
for columna in range(1, 11):
linea += f"{fila * columna:>4}"
print(linea) 1 2 3 4 5 6 7 8 9 10
--------------------------------------------
1 1 2 3 4 5 6 7 8 9 10
2 2 4 6 8 10 12 14 16 18 20
3 3 6 9 12 15 18 21 24 27 30
4 4 8 12 16 20 24 28 32 36 40
5 5 10 15 20 25 30 35 40 45 50
6 6 12 18 24 30 36 42 48 54 60
7 7 14 21 28 35 42 49 56 63 70
8 8 16 24 32 40 48 56 64 72 80
9 9 18 27 36 45 54 63 72 81 90
10 10 20 30 40 50 60 70 80 90 100Tres detalles que hacen que la salida quede cuadrada:
{...:>4}reserva cuatro caracteres para cada número y lo alinea a la derecha. Cuatro bastan porque el mayor valor, 100, tiene tres cifras, y el cuarto hueco es el espacio de separación.f"{'':>4}"es un hueco vacío de cuatro caracteres, para que el encabezado empiece desplazado y sus números caigan encima de la columna que les toca.- El separador mide 44 caracteres: 4 de la columna de la izquierda más 4 por cada una de las diez columnas.
Dibujar figuras#
Los bucles anidados son la forma clásica de practicar esto, porque obligan a razonar sobre la relación entre la fila y el número de columnas.
for fila in range(1, 6):
linea = ""
for columna in range(fila):
linea += "*"
print(linea)*
**
***
****
*****La clave está en el range(fila) del bucle interior: el número de vueltas del interior depende de la variable del exterior. En la primera fila da una vuelta, en la segunda dos, y así. Cuando los dos bucles son independientes sale un rectángulo; cuando el interior depende del exterior, salen triángulos y escaleras.
Este ejemplo, de hecho, se puede escribir en dos líneas aprovechando que multiplicar una cadena la repite:
for fila in range(1, 6):
print("*" * fila)Y eso es lo honesto: si hay una forma sin anidar, úsala. Los bucles anidados son necesarios cuando cada celda es distinta, no cuando la fila entera es el mismo carácter repetido.
Para una pirámide sí hay que calcular dos cosas por fila, los espacios de la izquierda y los asteriscos:
ALTURA = 5
for fila in range(1, ALTURA + 1):
espacios = " " * (ALTURA - fila)
asteriscos = "*" * (2 * fila - 1)
print(espacios + asteriscos) *
***
*****
*******
*********Y cuando cada celda depende de su posición, el anidamiento es imprescindible:
for fila in range(3):
linea = ""
for columna in range(6):
if (fila + columna) % 2 == 0:
linea += "#"
else:
linea += "."
print(linea)#.#.#.
.#.#.#
#.#.#.break solo sale del bucle en el que está#
Esto es lo que más confunde. Un break dentro del bucle interior termina el interior, y el exterior sigue dando vueltas como si nada:
for fila in range(1, 4):
for columna in range(1, 6):
if columna == 3:
break
print(f"fila {fila}, columna {columna}")
print("Terminado.")fila 1, columna 1
fila 1, columna 2
fila 2, columna 1
fila 2, columna 2
fila 3, columna 1
fila 3, columna 2
Terminado.Tres filas, y en cada una el break ha cortado el recorrido de las columnas en la tercera. El bucle exterior no se ha enterado.
Lo mismo vale para continue: afecta solo al bucle que lo contiene directamente.
Ejemplo completo#
Una tabla de precios con descuentos por volumen, que junta el anidamiento, el formato de columnas y los porcentajes:
PRECIO_BASE = 24.90
encabezado = f"{'Unidades':<10}"
for descuento in range(0, 25, 5):
encabezado += f"{descuento:>8}%"
print(encabezado)
print("-" * 55)
for unidades in range(1, 6):
linea = f"{unidades:<10}"
for descuento in range(0, 25, 5):
total = PRECIO_BASE * unidades * (1 - descuento / 100)
linea += f"{total:>9.2f}"
print(linea)Unidades 0% 5% 10% 15% 20%
-------------------------------------------------------
1 24.90 23.65 22.41 21.16 19.92
2 49.80 47.31 44.82 42.33 39.84
3 74.70 70.96 67.23 63.49 59.76
4 99.60 94.62 89.64 84.66 79.68
5 124.50 118.27 112.05 105.83 99.60