Control de flujo

Ejecutar este documento en forma dinámica:Binder

Para implementar un algoritmo en un programa, suele ser necesario realizar operaciones que alteran el flujo secuencial de un programa. Estas alteraciones se dividen principalmente en:

  • Bifurcaciones: según alguna condición, ejecutar un bloque de instrucciones u otro.
  • Repeticiones: ejecutar repetidamente un bloque de código hasta que se verifique una condición.

En este notebook exploraremos bifurcaciones y repeticiones con if/elif/else y for/while, así como la evaluación de condiciones.

if/elif/else

En un script de Python, la instrucción if es la forma de decidir la ejecución condicional de una instrucción o bloque de código, basado en la evaluación de una expresión. En su forma más simple, la sintaxis es:

if <expresión>:
    <instrucción>

Aquí, <expresión> es una expresión que debe ser evaluada en un contexto booleano, tal como se mostró en Variables y tipos de datos. <instrucción> debe ser una instrucción válida de Python, que debe estar indentada (anglicismo que proviene de la palabra inglesa indentation, y que en español se utiliza como sangrado y que significa mover un bloque de texto hacia la derecha insertando espacios o tabuladores).

Si <expresión> es evaluada como verdadera (True), se ejecuta <instrucción>, mientras que si es falsa (False), <instrucción> no se ejecuta. Notar que es obligatorio utilizar : luego de <expresión>. Veamos algunos ejemplos.

In [1]:
x = 0
y = 4
In [2]:
if x < y:
    print('Si')
Si
In [3]:
if y < x:
    print('No')
In [4]:
if x:
    print('x')
In [5]:
if y:
    print('y')
y
In [6]:
if x or y:
    print('alguno')
alguno
In [7]:
if x and y:
    print('ninguno')
In [8]:
if 'azul' in ['rojo', 'verde', 'azul']:
    print('color')
color
In [10]:
if 'blanco' in ['rojo', 'verde', 'azul']:
    print('nada')

En estos ejemplos solo utilizamos una única instrucción a ejecutar si la condición evalúa a True. Es posible ejecutar condicionalmente un conjunto de intrucciones, o bloque de código, manteniendo cada instrucción perteneciente al bloque con la misma indentación. Es decir, la indentación define un bloque de código.

En el caso de una instrucción if con múltiples instrucciones, la sintaxis es:

if <expresión>:
    <instrucción-1>
    <instrucción-2>
    ...
    <instrucción-n>
<instrucción_siguiente>

Aquí, todas las instrucciones desde la 1 a la n pertenecen al mismo bloque de código, y son ejecutadas secuencialmente si <expresión> evalúa a True. Al finalizar la ejecución del bloque, el programa sigue su flujo ejecutando <instrucción_siguiente>, que ya no pertenece al bloque al no estar al mismo nivel de indentación. En caso que <expresión> evalúe a False, el bloque no se ejecuta y el programa pasa directamente a la ejecución de <intrucción_siguiente>. Por ejemplo:

In [15]:
print(x,y)
if x:  # Cambiar x por y luego de la primera ejecución
    y = y + 1
    print(x,y)
print(x + y)
0 5
5

En algunas situaciones, lo que se necesita es evaluar una condición y ejecutar un bloque de código si la condición es True, y otro bloque de código si la condición es False. Esto se logra con la cláusula else:

if <expresión>:
    <bloque_1>
else:
    <bloque_2>

Si <expresión> es True, el primer bloque <bloque_1> se ejecuta y no el segundo, y si <expresión> es False, entonces <bloque_1> no se ejecuta y se ejecuta <bloque_2>. Por supuesto, ambos bloques de código quedan definidos por su indentación. Veamos un ejemplo:

In [18]:
x = 30
if x < 20:
    print('x es menor que 20')
    print('x es pequeño')
else:
    print('x es mayor o igual a 20')
    print('x es grande')
x es mayor o igual a 20
x es grande

Python ofrece una sintaxis para una ejecución ramificada basada en varias alternativas, utilizando la cláusula elif (una forma abreviada de else if). Python evalúa cada <expresión> y ejecuta el bloque de código correspondiente a la primera expresión que resulta True. Si ninguna de las expresiones es verdadera, y se especifica una cláusula else, entonces se ejecuta el bloque correspondiente (la cláusula else es opcional, solo puede haber una y ubicada al final):

In [27]:
a = 1
if a == 1:
    print(1)
elif a == 2:
    print(2)
elif a == 3:
    print(3)
else:
    print('a > 3')
1

Expresiones condicionales

Python permite una estructura denominada expresión condicional (referida también como operador condicional u operador ternario), cuya forma más simple tiene la siguiente sintaxis:

<expresión_1> if <expresión_condicional> else <expresión_2>

Este if es diferente al anterior dado que no resulta en un estructura de control que dirige el flujo de la ejecución de un programa, sino que actúa más como un operador que define una expresión. En el ejemplo anterior, primero se evalúa <expresión_condicional>. Si es True, la expresión evalúa a <expresión_1>, y si es False a <expresión_2>. Hay que tener en cuenta que el orden no es obvio: primero se evalúa la expresión del medio, y según el resultado se devuelve una de las expresiones de los extremos:

In [31]:
llueve = True
print('Vamos a', 'la playa' if not llueve else 'el bar')
Vamos a el bar

Las expresiones condicionales pueden reemplazarse por un if/else:

In [32]:
a, b = 3, 7
if a > b:
    m = a
else:
    m = b
print(m)
7

Pero una expresión condicional es más corta y más expresiva:

In [34]:
a, b = 7, 3
m = a if a > b else b
print(m)
7

Iteraciones con for

Iterar significa ejecutar el mismo bloque de código reiteradamente, potencialmente muchas veces. Una estructura de programación que implementa iteraciones se denomina bucle (o loop).

En programación existen dos tipos de iteración: la definida o la indefinda.

La iteración definida consiste en la ejecución de un bloque de código un número establecido de veces, explícitamente definido cuando el se inicia el bucle.

En una iteración indefinida, el número de veces que se ejecutará el bloque no está explícitamente definido de antemano, sino que el bloque se ejecuta repetidamente hasta que alguna condición se satisfaga (o eventualmente la repetición puede no finalizar nunca).

En Python, la sentencia for itera sobre los elementos de cualquier secuencia (una lista, tupla o string), en el mismo orden en que aparecen en la secuencia. Por ejemplo:

In [39]:
herramientas = ['destornillador', 'pinza', 'martillo']
for h in herramientas:
    print(h, len(h))
destornillador 14
pinza 5
martillo 8

Si en el bucle es necesario modificar la secuencia sobre la que se itera (por ejemplo, eliminar un elemento), es recomendable hacer primero una copia. Iterar sobre una secuencia no hace una copia, pero utilizando la notación de rebanado sí:

In [40]:
for h in herramientas[:]:
    if len(h) > 7:
        herramientas.insert(0, h)
herramientas
Out[40]:
['martillo', 'destornillador', 'destornillador', 'pinza', 'martillo']

Al hacer esto sin copiar primero la lista, por ejemplo

for h in herramientas:
    ...

el programa intentaría crear una lista infinita, insertando martillo una y otra vez:

In [41]:
for h in herramientas:
    if len(h) > 7:
        herramientas.insert(0, h)
herramientas
---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
<ipython-input-41-7dc34c08c0c3> in <module>
      1 for h in herramientas:
      2     if len(h) > 7:
----> 3         herramientas.insert(0, h)
      4 herramientas

KeyboardInterrupt: 

Los bucles pueden anidarse de modo de producir iteraciones dentro de iteraciones:

In [45]:
herramientas = ['destornillador', 'pinza', 'martillo']
for h in herramientas:
    print(h)
    palabra = ''
    for l in h:
        palabra += l + '-'
    print(palabra)
destornillador
d-e-s-t-o-r-n-i-l-l-a-d-o-r-
pinza
p-i-n-z-a-
martillo
m-a-r-t-i-l-l-o-

Para iterar sobre una secuencia de números, Python provee la función range(), que genera progresiones aritméticas:

In [46]:
for i in range(5):
    print(i)
0
1
2
3
4

help(range)

La sintaxis de esta función es range(inicio, fin, paso), donde inicio, fin y paso son enteros (inicio y paso son opcionales, con valores por defecto 0 y 1 respectivamente). El valor de fin nunca es parte de la secuencia. Ejemplos:

In [50]:
for i in range(3, 10, 2):
    print(i)
3
5
7
9

Es posible iterar sobre los índices de una secuencia, combinando range y len:

In [52]:
for i in range(len(herramientas)):
    print (i, herramientas[i])
0 destornillador
1 pinza
2 martillo

sin emabrgo, en muchos casos es mejor utilizar enumerate(), que devuelve el índice de la posición del elemento de la lista junto con su valor:

In [53]:
for i, h in enumerate(herramientas):
    print(i,h)
0 destornillador
1 pinza
2 martillo

Es posible iterar sobre dos secuencias al mismo tiempo, emparejando sus valores con la función zip:

In [60]:
preguntas = ['nombre', 'interno', 'función']
respuestas = ['Nicasio', 221, 'ingeniero']
for p, r in zip(preguntas, respuestas):
    print('¿Cuál es tu {0}? - {1}'.format(p,r))
¿Cuál es tu nombre? - Nicasio
¿Cuál es tu interno? - 221
¿Cuál es tu función? - ingeniero

En el caso de iteración sobre diccionarios, el métodoitems() devuelve la clave y su valor correspondiente:

In [58]:
stock = {'clavos': 2000, 'tuercas': 1200, 'tornillos': 45}
for k, v in stock.items():
    print('Tenemos {1} de {0}'.format(k, v))
Tenemos 2000 de clavos
Tenemos 1200 de tuercas
Tenemos 45 de tornillos

Iteraciones con while

En Python pueden generarse iteraciones indefinidas por medio de la instrucción while. La sintaxis es:

while <expresión>:
    <bloque de código>

Cuando aparece un bucle while, se evalúa <expresión> en un contexto booleano. Generalmente esta expresión involucra una o más variables incializadas previamente y que luego son modificadas en alguna instrucciones del bloque de código a iterar (generalmente denominado cuerpo del bucle). Si la evaluación resulta True, el cuerpo es ejecutado, y luego <expresión> es evaluada nuevamente. Este proceso continua mientras la expresión evaluada sea verdadera. Cuando devuelve False, el programa continua con la primera instrucción que sigue al cuerpo del bucle.

Ejemplo:

In [61]:
n = 5
while n > 0:
    print(n)
    n -= 1
5
4
3
2
1

Notar que la expresión que controla el bucle es lo primero que se evalúa, y si inicialmente es falsa no se ejecuta el cuerpo del bucle (ejecutar el código anterior con un valor de n que haga falsa la evaluación).

La condición que regula un while funciona con todo lo que devuelva un True o False, por ejemplo se puede usar una lista como en el siguiente ejemplo:

In [62]:
a = ['Uno', 'Dos', 'Tres']
while a:
    print(a.pop(-1))
Tres
Dos
Uno

Python permite utilizar la cláusula opcional else al final de un bucle while. La sintaxis es la siguiente:

while <expresión>:
    <bloque_de_código>
else:
    <instrucciones_adicionales>

Las <instrucciones_adicionales> se ejecutan cuando finaliza el bucle, siempre que la salida del bucle sea porque la expresión que lo controla evalúa a False. (Veremos un poco más abajo que es posible "escapar" del bucle con una instrucción break, y en ese caso no se ejecutan las <instrucciones_adicionales>). Por ejemplo:

In [64]:
a = 5
while a > 0:
    print(a)
    a -= 1
else:
    print('Bucle finalizado')
5
4
3
2
1
Bucle finalizado

Interrupciones de iteraciones

En los ejemplos que vimos hasta ahora, las iteraciones se repiten mientras la condición que evalúa el bucle sea verdadera (o mientras haya elementos para iterar en el caso de for). Python provee dos keywords que permite terminar una iteración prematuramente:

  • break inmediatamente finaliza el bucle, y el programa continua con la instrucción siguiente a la del cuerpo del loop.
  • continue finaliza inmediatamente la iteración en curso, y vuelve a evaluar la condición de finalización.

Veamos algunos ejemplos:

In [65]:
n = 5
while n > 0:
    n -= 1
    if n == 2:
        break
    print(n)
print('Bucle finalizado')
4
3
Bucle finalizado
In [66]:
n = 5
while n > 0:
    n -= 1
    if n == 2:
        continue
    print(n)
print('Bucle finalizado')
4
3
1
0
Bucle finalizado

Combinando los ejemplos anteriores, podemos ver en qué casos un else de un bucle con while no se ejecuta:

In [67]:
n = 5
while n > 0:
    n -= 1
    print(n)
    if n == 2:
        break
else:
     print('Bucle finalizado')
4
3
2
In [69]:
herramientas = ['destornillador', 'pinza', 'martillo']
for h in herramientas:
    print(h)
    if h == 'pinza':
        print('Tenemos pinza!')
        break
destornillador
pinza
Tenemos pinza!

break permite también escapar de bucles infinitos. Tales bucles se producen cuando la condición de evaluación siempre es verdadera:

while True:
    print('nada')

Este código se ejecutará para siempre imprimiendo nada en la pantalla. En este contexto, "para siempre" significa hasta que lo cortemos con Ctrl C o se termine el suministro eléctrico. Pareciera poco práctico generar un bucle infinito, pero puede ser útil cuando queremos que algo se ejecute mientras controlamos la finalización con un break. Por ejemplo:

In [70]:
a = ['uno', 'dos', 'tres', 'cuatro']
while True:
    if not a:
        break
    print(a.pop())
cuatro
tres
dos
uno

Comprensiones de listas

Una manera muy práctica de iterar al tiempo en que se genera una lista es lo que se denomina comprensión de listas:

In [71]:
[i**2 for i in range(4)]
Out[71]:
[0, 1, 4, 9]
In [ ]: