Introducción a la programación en Python

clase 03b
Programación Estructurada

Programación estructurada

La programación estructurada es un paradigma de programación basado en utilizar funciones o subrutinas, y únicamente tres estructuras de control:

  • secuencia: ejecución de una instrucción a continuación de otra.
  • selección o condicional: ejecución de una instrucción o conjunto de instrucciones, según el valor de una variable booleana (si una condición es verdadera o falsa).
  • iteración (ciclo, bucle o loop): ejecución de una instrucción o conjunto de instrucciones, una cierta cantidad de veces o mientras se cumpla una condición.

La programación estructurada se fundamenta en el teorema correspondiente, que establece que toda función computable puede ser implementada en un lenguaje de programación que combine sólo estas tres estructuras lógicas o de control.

secuencia

La estructura de secuencia es la que se da naturalmente en el lenguaje, ya que por defecto las sentencias son ejecutadas en el orden en que aparecen escritas en el programa.

selección o condicional

Para las estructuras condicionales o de selección, Python dispone de la instrucción if, que puede combinarse con instrucciones elif y/o else.

if

La instrucción if verifica el valor de verdad de una expresión, y si es verdadera ejecuta el código correspondiente.

El bloque de código a ejecutarse debe ir indentado.

La sintaxis es:

if condición:
    bloque de código (indentado)

La condición debe ser cualquier expresión que devuelva el valor True o False.
Por ejemplo, pueden usarse cualquiera de los operadores de comparación vistos en la clase 1.

In [23]:
a = 5
if a > 0:
    print("a es positivo")
a es positivo

Las comparaciones pueden combinarse con los operadores booleanos and, or y not.

In [24]:
a = 5
if a >= 0 and a < 10:
    print("a es un número entre 0 y 9")
a es un número entre 0 y 9
In [25]:
a = 130
if a < 0 or a > 127:
    print("a está fuera del rango de notas MIDI")
a está fuera del rango de notas MIDI

Ejemplo 3.1

Vamos a definir una función que, dada una altura en número de nota MIDI, verifica si se encuentra dentro de un rango determinado (por defecto, el rango completo de notas MIDI, 0 a 127).

In [26]:
def nota_en_rango(nota, lim_inf=0, lim_sup=127):
    """Verifica si una altura en nota MIDI está dentro de un rango"""
    
    if nota < lim_inf or nota > lim_sup:
        # imprime una advertencia si la nota está fuera de rango.
        print("ATENCIÓN: la nota {} está fuera del rango {} - {}".format(nota, lim_inf, lim_sup))
        

nota = 88
nota_en_rango(nota)
nota_en_rango(nota, 55, 80)
ATENCIÓN: la nota 88 está fuera del rango 55 - 80

if - else

La instrucción if puede opcionalmente combinarse con la instrucción else, que ejecuta un bloque de código alternativo, cuando no se cumple la condición del if.

Por ejemplo, la función anterior solamente imprime una advertencia cuando la altura está fuera de rango; en caso contrario, pasa silenciosamente. Podemos completar la estructura if con un else, para que la función reporte un resultado en todos los casos.

Ejemplo 3.1b

In [27]:
def nota_en_rango(nota, lim_inf=0, lim_sup=127):
    """Verifica si una altura en nota MIDI está dentro de un rango"""
    
    if nota < lim_inf or nota > lim_sup: # imprime una advertencia si la nota está fuera de rango.
        print("ATENCIÓN: la nota {} está fuera del rango {} - {}".format(nota, lim_inf, lim_sup))
    else: # en caso contrario, devuelve un OK
        print("nota {}: OK".format(nota))
        

nota = 88
nota_en_rango(nota)
nota_en_rango(nota, 55, 80)
nota 88: OK
ATENCIÓN: la nota 88 está fuera del rango 55 - 80

if - elif

Las estructuras condicionales if también pueden combinarse con una o más instrucciones elif, que es una abreviación de else, if.
Cada elif agrega una nueva condición a probar, si no se cumple la condición del if o elif anterior.

La función del ejemplo anterior se puede ampliar para que primero chequee si la nota está por debajo del límite inferior, y en caso contrario, ver si excede el límite superior. De no cumplirse ninguna de las dos condiciones, se ejecuta el else.

Ejemplo 3.1c

In [28]:
def nota_en_rango(nota, lim_inf=0, lim_sup=127):
    """Verifica si una altura en nota MIDI está dentro de un rango"""
    
    if nota < lim_inf: # imprime una advertencia si está debajo del límite inferior
        print("ATENCIÓN: la nota {} está por debajo del límite inferior".format(nota))
    elif nota > lim_sup: # imprime una advertencia si encima del límite superior
        print("ATENCIÓN: la nota {} está por encima del límite superior".format(nota))
    else: # en caso contrario, devuelve un OK
        print("nota {}: OK".format(nota))
        

nota1 = 88
nota2 = -3
nota3 = 132
nota_en_rango(nota1)
nota_en_rango(nota2)
nota_en_rango(nota3)
nota 88: OK
ATENCIÓN: la nota -3 está por debajo del límite inferior
ATENCIÓN: la nota 132 está por encima del límite superior

El esquema de una estructura completa con un if, uno o más elif, y un else, sería la siguiente:

if condición1:
    bloque de código 1 (indentado)
elif condición2:
    bloque de código 2 (indentado)
·
·
·
else:
    bloque de código N (indentado)

iteración o bucle (loop)

Para las iteraciones o bucles (loops) existen las estructuras while y for.

while

La instrucción while permite implementar un ciclo o bucle: verifica el valor de una condición, y si ésta es verdadera, ejecuta el código que sigue. El proceso se repite hasta que la expresión condicional sea falsa.

El esquema de la sintaxis es:

while condición:
    bloque de código (indentado)
In [29]:
contador = 0
while contador <= 5:
    print(contador, end=' ')
    contador = contador+1    # se incrementa el valor de la variable
0 1 2 3 4 5 

Es importante asegurarse que en algún momento la expresión condicional se vuelva falsa, de lo contrario el programa quedará estancado en un bucle sin fin. En el ejemplo anterior tomamos esa precaución, incrementado el valor de la variable contador en cada pasada.

asignación aumentada

Es frecuente que una variable tenga que ser redefinida en función de sí misma, como en el ejemplo anterior, donde a la variable contador se le sumaba 1 en cada pasada. En vez de escribir:

contador = contador + 1

se puede abreviar a su equivalente:

contador += 1

que no sólo es más corto de escribir, sino también más eficiente.

Existe en Python todo un conjunto de operadores de este tipo, llamados de asignación aumentada, entre los que podemos mencionar:

X += Y    ( X = X + Y )
X −= Y    ( X = X - Y )
X *= Y    ( X = X * Y )
X **= Y   ( X = X ** Y )
X /= Y    ( X = X / Y )
X //= Y   ( X = X // Y )
X %= Y    ( X = X % Y )

while - else

Al igual que la estructura if, la estructura while también puede combinarse con una instrucción else. El bloque de código a continuación de la instrucción else se ejecutará cuando la expresión condicional del while sea falsa.

El nombre de la instrucción es equívoco, ya que el bloque del else se ejecutará en todos los casos; tiene la ventaja de mantener el mismo nombre y la misma sintaxis que en las demás estructuras de control.

En el ejemplo anterior podemos agregar una instrucción else al final, que ejecutará el código siguiente cuando la condición del while sea falsa.

In [30]:
contador = 0
while contador <= 5:
    print(contador, end=' ')
    contador +=1    # se incrementa el valor de la variable
else:
    print("\nno se ejecuta el while, el contador es", contador)
0 1 2 3 4 5 
no se ejecuta el while, el contador es 6

Ejemplo 3.2

La siguiente función calcula todos los términos de la serie de Fibonacci, que no sobrepasen un número límite que se da como argumento.

Se utiliza la posibilidad que ofrece Python de asignación múltiple, lo que simplifica el código.

var1, var2 = expr1, expr2
In [31]:
def fib(n):
    """Imprime una serie de Fibonacci hasta el número n."""
    a, b = 0, 1
    while a <= n:
        print(a, end=' ')
        a, b = b, a+b
    print()    # imprime un salto de línea cuando termina

fib(1000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 

for

La instrucción for implementa un bucle, recorriendo ordenadamente una secuencia, o cualquier otro objeto iterable.

La sintaxis básica es la siguiente:

for i in s:
    bloque de código
  • s puede ser cualquier objeto iterable, como una lista, una cadena de caracteres, u otros.
  • cada ítem de s es asignado, por orden, a la variable i, y se ejecuta el bloque de código.
  • es habitual que el bloque de código utilice la variable i, con diferente resultado para cada ítem del objeto s.
  • la estructura for concluye cuando se termina de recorrer todos los elementos de s.
In [32]:
for item in [9, 7, 13, 22]:
    print(item, end=' ')
9 7 13 22 
In [33]:
for letra in "hola":
    print(letra*5, end='')
hhhhhooooolllllaaaaa

for - else

Al igual que en las estructuras de bucle creadas con la instrucción while, acá se puede utilizar la instrucción else para ejecutar un bloque de código una vez que se haya terminado la iteración.

In [34]:
frutas_en_la_heladera = ['una manzana', 'dos naranjas', 'un durazno']
for i in frutas_en_la_heladera:
    print("como", i)
else:
    print("se terminaron las frutas...")    
como una manzana
como dos naranjas
como un durazno
se terminaron las frutas...

range()

En las estructuras for es frecuente utilizar la función range(), que genera una secuencia de valores enteros entre un valor inicial y un límite superior (sin uncluirlo), con un determinado incremento.

Su sintaxis completa es:

range(valor_inicial, limite_superior, [incremento])

  • por defecto, el incremento es 1.
  • si se pasa un solo argumento, se considera como el límite superior, siendo el valor inicial cero.

Por lo tanto, range(10) equivale a range(0, 10, 1).

In [35]:
for i in range(5): # de 0 a 5 (excluyente), con incremento 1
    print(i, end=' ')
0 1 2 3 4 
In [36]:
for i in range(5,10): # de 5 a 10 (excluyente), con incremento 1
    print(i, end=' ')
5 6 7 8 9 
In [37]:
for i in range(0,20,5): # de 0 a 20 (excluyente), con incremento 5
    print(i, end=' ')
0 5 10 15 

list()

Aunque tiene la forma de una función, list() es un constructor, que crea una lista a partir de un objeto iterable.
Si el objeto ya es una lista, simplemente se crea una copia.
Pero puede ser otro tipo de secuencia, como una cadena str, o una secuencia generada por la función range().

In [38]:
b = list("¡hola!")
print(b)
['¡', 'h', 'o', 'l', 'a', '!']
In [39]:
a = list(range(5, -5, -1))
print(a)
[5, 4, 3, 2, 1, 0, -1, -2, -3, -4]