Bucles

../../../_images/gary-lopater-UaUaefoUmZ8-unsplash.jpg

Cuando queremos hacer algo más de una vez, necesitamos recurrir a un bucle. En esta sección veremos las distintas sentencias en Python que nos permiten repetir un bloque de código. 1

La sentencia while

El primer mecanismo que existe en Python para repetir instrucciones es usar la sentencia while. La semántica tras esta sentencia es: «Mientras se cumpla la condición haz algo». Veamos un sencillo bucle que muestra por pantalla los números del 1 al 4:

>>> value = 1

>>> while value <= 4:
...     print(value)
...     value += 1
...
1
2
3
4

Ejecución paso a paso a través de Python Tutor:

La condición del bucle se comprueba en cada nueva repetición. En este caso chequeamos que la variable value sea menor o igual que 4. Dentro del cuerpo del bucle estamos incrementando esa variable en 1 unidad.

Romper un bucle while

Python ofrece la posibilidad de romper o finalizar un bucle antes de que se cumpla la condición de parada. Supongamos un ejemplo en el que estamos buscando el primer número múltiplo de 3 yendo desde 20 hasta 1:

>>> num = 20

>>> while num >= 1:
...     if num % 3 == 0:
...         print(num)
...         break
...     num -= 1
...
18

Ejecución paso a paso a través de Python Tutor:

Como hemos visto en este ejemplo, break nos permite finalizar el bucle una vez que hemos encontrado nuestro objetivo: el primer múltiplo de 3. Pero si no lo hubiéramos encontrado, el bucle habría seguido decrementando la variable num hasta valer 0, y la condición del bucle while hubiera resultado falsa.

Comprobar la rotura

Nivel intermedio

Python nos ofrece la posibilidad de detectar si el bucle ha acabado de forma ordinaria, esto es, ha finalizado por no cumplirse la condición establecida. Para ello podemos hacer uso de la sentencia else como parte del propio bucle. Si el bucle while finaliza normalmente (sin llamada a break) el flujo de control pasa a la sentencia opcional else.

Veamos un ejemplo en el que tratamos de encontrar un múltiplo de 9 en el rango \([1, 8]\) (es obvio que no sucederá):

>>> num = 8

>>> while num >= 1:
...     if num % 9 == 0:
...         print(f'{num} is a multiple of 9!')
...         break
...     num -= 1
... else:
...     print('No multiples of 9 found!')
...
No multiples of 9 found!

Ejecución paso a paso a través de Python Tutor:

Continuar un bucle

Nivel intermedio

Hay situaciones en las que, en vez de romper un bucle, nos interesa saltar adelante hacia la siguiente repetición. Para ello Python nos ofrece la sentencia continue que hace precisamente eso, descartar el resto del código del bucle y saltar a la siguiente iteración.

Veamos un ejemplo en el que usaremos esta estrategia para mostrar todos los números en el rango \([1, 20]\) ignorando aquellos que sean múltiplos de 3:

>>> num = 21

>>> while num >= 1:
...     num -= 1
...     if num % 3 == 0:
...         continue
...     print(num, end=', ')  # Evitar salto de línea
...
20, 19, 17, 16, 14, 13, 11, 10, 8, 7, 5, 4, 2, 1,

Ejecución paso a paso a través de Python Tutor:

Bucle infinito

Si no establecemos correctamente la condición de parada o bien el valor de alguna variable está fuera de control, es posible que lleguemos a una situación de bucle infinito, del que nunca podamos salir. Veamos un ejemplo de esto:

>>> num = 1

>>> while num != 10:
...     num += 2
...
# CTRL-C
KeyboardInterrupt
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>

El problema que surje es que la variable num toma los valores 1, 3, 5, 7, 9, 11, ... por lo que nunca se cumple la condición de parada del bucle. Esto hace que repitamos «eternamente» la instrucción de incremento.

Ejecución paso a paso a través de Python Tutor:

Una posible solución a este error es reescribir la condición de parada en el bucle:

>>> num = 1

>>> while num < 10:
...     num += 2
...

Truco

Para abortar una situación de bucle infinito podemos pulsar en el teclado la combinación CTRL-C. Se puede ver reflejado en el intérprete de Python por KeyboardInterrupt.

Ejercicio

Escriba un programa que calcule la distancia hamming entre dos cadenas de texto de la misma longitud.

Ejemplo
  • Entrada: 0001010011101 y 0000110010001

  • Salida: 4

Solución: hamming.py

La sentencia for

Python permite recorrer aquellos tipos de datos que sean iterables, es decir, que admitan iterar 2 sobre ellos. Algunos ejemplos de tipos y estructuras de datos que permiten ser iteradas (recorridas) son: cadenas de texto, listas, diccionarios, ficheros, etc. La sentencia for nos permite realizar esta acción.

A continuación se plantea un ejemplo en el que vamos a recorrer (iterar) una cadena de texto:

>>> word = 'Python'

>>> for letter in word:
...     print(letter)
...
P
y
t
h
o
n

La clave aquí está en darse cuenta que el bucle va tomando, en cada iteración, cada uno de los elementos de la variable que especifiquemos. En este caso concreto letter va tomando cada una de las letras que existen en word, porque una cadena de texto está formada por elementos que son caracteres.

Ejecución paso a paso a través de Python Tutor:

Importante

La variable que utilizamos en el bucle for para ir tomando los valores puede tener cualquier nombre. Al fin y al cabo es una variable que definimos según nuestras necesidades. Tener en cuenta que se suele usar un nombre en singular.

Romper un bucle for

Una sentencia break dentro de un for rompe el bucle, igual que veíamos para los bucles while. Veamos un ejemplo con el código anterior. En este caso vamos a recorrer una cadena de texto y pararemos el bucle cuando encontremos una letra t minúscula:

>>> word = 'Python'

>>> for letter in word:
...     if letter == 't':
...         break
...     print(letter)
...
P
y

Ejecución paso a paso a través de Python Tutor:

Truco

Tanto la comprobación de rotura de un bucle como la continuación a la siguiente iteración se llevan a cabo del mismo modo que hemos visto con los bucles de tipo while.

Ejercicio

Dada una cadena de texto, indique el número de vocales que tiene.

Ejemplo
  • Entrada: Supercalifragilisticoespialidoso

  • Salida: 15

Solución: num_vowels.py

Secuencias de números

Es muy habitual hacer uso de secuencias de números en bucles. Python no tiene una instrucción específica para ello. Lo que sí aporta es una función range() que devuelve un flujo de números en el rango especificado. Una de las grandes ventajas es que la «lista» generada no se construye explícitamente, sino que cada valor se genera bajo demanda. Esta técnica mejora el consumo de recursos, especialmente en términos de memoria.

La técnica para la generación de secuencias de números es muy similar a la utilizada en los «slices» de cadenas de texto. En este caso disponemos de la función range(start, stop, step):

  • start: Es opcional y tiene valor por defecto 0.

  • stop: es obligatorio (siempre se llega a 1 menos que este valor).

  • step: es opcional y tiene valor por defecto 1.

range() devuelve un objeto iterable, así que iremos obteniendo los valores paso a paso con una sentencia for ... in 3. Veamos diferentes ejemplos de uso:

Rango: \([0, 1, 2]\)
>>> for i in range(0, 3):
...     print(i)
...
0
1
2

>>> for i in range(3):
...     print(i)
...
0
1
2
Rango: \([1, 3, 5]\)
>>> for i in range(1, 6, 2):
...     print(i)
...
1
3
5
Rango: \([2, 1, 0]\)
>>> for i in range(2, -1, -1):
...     print(i)
...
2
1
0

Ejecución paso a paso a través de Python Tutor:

Truco

Se suelen utilizar nombres de variables i, j, k para lo que se denominan contadores. Este tipo de variables toman valores numéricos enteros como en los ejemplos anteriores. No conviene generalizar el uso de estas variables a situaciones en las que, claramente, tenemos la posibilidad de asignar un nombre semánticamente más significativo. Esto viene de tiempos antiguos en FORTRAN donde i era la primera letra que tenía valor entero por defecto.

Ejercicio

Determine si un número dado es un número primo.

No es necesario implementar ningún algoritmo en concreto. La idea es probar los números menores al dado e ir viendo si las divisiones tienen resto cero o no.

¿Podrías optimizar tu código? ¿Realmente es necesario probar con tantos divisores?

Ejemplo
  • Entrada: 11

  • Salida: Es primo

Solución: prime.py

Usando el guión bajo

Nivel avanzado

Hay situaciones en las que no necesitamos usar la variable que toma valores en el rango, sino que únicamente queremos repetir una acción un número determinado de veces.

Para estos casos se suele recomendar usar el guión bajo _ como nombre de variable, que da a entender que no estamos usando esta variable de forma explícita:

>>> for _ in range(10):
...     print('Repeat me 10 times!')
...
Repeat me 10 times!
Repeat me 10 times!
Repeat me 10 times!
Repeat me 10 times!
Repeat me 10 times!
Repeat me 10 times!
Repeat me 10 times!
Repeat me 10 times!
Repeat me 10 times!
Repeat me 10 times!

Bucles anidados

Como ya vimos en las sentencias condicionales, el anidamiento es una técnica por la que incluimos distintos niveles de encapsulamiento de sentencias, unas dentro de otras, con mayor nivel de profundidad. En el caso de los bucles también es posible hacer anidamiento.

Veamos un ejemplo de 2 bucles anidados en el que generamos todas las tablas de multiplicar:

>>> for i in range(1, 10):
...     for j in range(1, 10):
...         result = i * j
...         print(f'{i} * {j} = {result}')
...
1 x 1 = 1
1 x 2 = 2
1 x 3 = 3
1 x 4 = 4
1 x 5 = 5
1 x 6 = 6
1 x 7 = 7
1 x 8 = 8
1 x 9 = 9
2 x 1 = 2
2 x 2 = 4
2 x 3 = 6
2 x 4 = 8
2 x 5 = 10
2 x 6 = 12
2 x 7 = 14
2 x 8 = 16
2 x 9 = 18
3 x 1 = 3
3 x 2 = 6
3 x 3 = 9
3 x 4 = 12
3 x 5 = 15
3 x 6 = 18
3 x 7 = 21
3 x 8 = 24
3 x 9 = 27
4 x 1 = 4
4 x 2 = 8
4 x 3 = 12
4 x 4 = 16
4 x 5 = 20
4 x 6 = 24
4 x 7 = 28
4 x 8 = 32
4 x 9 = 36
5 x 1 = 5
5 x 2 = 10
5 x 3 = 15
5 x 4 = 20
5 x 5 = 25
5 x 6 = 30
5 x 7 = 35
5 x 8 = 40
5 x 9 = 45
6 x 1 = 6
6 x 2 = 12
6 x 3 = 18
6 x 4 = 24
6 x 5 = 30
6 x 6 = 36
6 x 7 = 42
6 x 8 = 48
6 x 9 = 54
7 x 1 = 7
7 x 2 = 14
7 x 3 = 21
7 x 4 = 28
7 x 5 = 35
7 x 6 = 42
7 x 7 = 49
7 x 8 = 56
7 x 9 = 63
8 x 1 = 8
8 x 2 = 16
8 x 3 = 24
8 x 4 = 32
8 x 5 = 40
8 x 6 = 48
8 x 7 = 56
8 x 8 = 64
8 x 9 = 72
9 x 1 = 9
9 x 2 = 18
9 x 3 = 27
9 x 4 = 36
9 x 5 = 45
9 x 6 = 54
9 x 7 = 63
9 x 8 = 72
9 x 9 = 81

Lo que está ocurriendo en este código es que, para cada valor que toma la variable i, la otra variable j toma todos sus valores. Como resultado tenemos una combinación completa de los valores en el rango especificado.

Ejecución paso a paso a través de Python Tutor:

Nota

  • Podemos añadir todos los niveles de anidamiento que queramos. Eso sí, hay que tener en cuenta que cada nuevo nivel de anidamiento supone un importante aumento de la complejidad ciclomática de nuestro código, lo que se traduce en mayores tiempos de ejecución.

  • Los bucles anidados también se pueden aplicar en la sentencia while.

Ejercicio

Imprima los 100 primeros números de la sucesión de Fibonacci: \(0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, \dots\)

Solución: fibonacci.py


EJERCICIOS DE REPASO

  1. Escriba un programa en Python que realice las siguientes 9 multiplicaciones. ¿Nota algo raro? (solución)

\[\begin{split}1 &\cdot 1\\ 11 &\cdot 11\\ 111 &\cdot 111\\ &\vdots\\ 111111111 &\cdot 111111111\end{split}\]
  1. Escriba un programa en Python que acepte una cadena de texto e indique si todos sus caracteres son alfabéticos (solución).

    Entrada: hello-world
    Salida: Se han encontrado caracteres no alfabéticos
  2. Escriba un programa en Python que acepte un número entero \(n\) y realice el siguiente cálculo de productos sucesivos (solución):

\[\prod_{i=1}^n x_i^2 = x_0^2 \cdot x_1^2 \cdot x_2^2 \cdot \cdots \cdot x_n^2\]
  1. Escriba un programa en Python que acepte dos cadenas de texto y compute el producto cartesiano letra a letra entre ellas (solución).

    Entrada: cadena1=abc; cadena2=123
    Salida: a1 a2 a3 b1 b2 b3 c1 c2 c3
  2. Escriba un programa en Python que acepte dos valores enteros (\(x\) e \(y\)) que representarán un punto (objetivo) en el plano. El programa simulará el movimiento de un «caballo» de ajedrez moviéndose de forma alterna: 2 posiciones en \(x\) + 1 posición en \(y\). El siguiente movimiento que toque sería para moverse 1 posición en \(x\) + 2 posiciones en \(y\). El programa deberá ir mostrando los puntos por los que va pasando el «caballo» hasta llegar al punto objetivo (solución).

    Entrada: objetivo_x=7; objetivo_y=8;
    Salida: (0, 0) (1, 2) (3, 3) (4, 5) (6, 6) (7, 8)

AMPLIAR CONOCIMIENTOS

1

Foto original de portada por Gary Lopater en Unsplash.

2

Realizar cierta acción varias veces. En este caso la acción es tomar cada elemento.

3

O convertir el objeto a una secuencia como una lista.