Listas e iteraciones#

Motivación: evaluar un polinomio de grado \(n\)#

En la sección anterior, escribimos la siguiente función, que nos permite evaluar un polinomio cuadrático \(f(x) = ax^2 + bx + c\):

def evaluar_cuadratica(x, a, b, c):
    return a * x**2 + b * x + c


evaluar_cuadratica(5, a=1, b=2, c=3)
38

¿Cómo podemos hacer para evaluar un polinomio de grado \(n\)?

Empecemos con el caso lineal, \(f(x) = ax + b\), y supongamos que queremos evaluar \(f(x) = 2 x + 3\) en \(x=1\).

Opción 1#

Podemos escribir otra función para evaluar un polinomio lineal:

def evaluar_lineal(x, a, b):
    return a * x + b


evaluar_lineal(1, a=2, b=3)
5

¿Vamos a tener que escribir una función para cada grado?

Debe haber una mejor manera…

Opción 2#

Una lineal es una cuadrática sin coeficiente cuadrático.

Entonces, podríamos reusar la función evaluar_cuadratica:

evaluar_cuadratica(1, a=0, b=2, c=3)
5

Llegamos al mismo resultado, y nos ahorramos escribir una nueva función. ¡Genial!

Pero… es confuso usar una función llamada evaluar_cuadratica para evaluar una lineal.

Además, hay que acordarse que el a de la lineal es el b de la cuadrática.

Y no nos sirve para polinomios de mayor grado.

Opción 3#

Si queremos escribir un código para polinomios de grado \(n\), veamos como es la fórmula para dichos polinomios.

Un polinomio de grado \(n\) se puede escribir como:

\[ f(x) = c_n x^n + c_{n-1} x^{n-1} + \ldots + c_2 x^2 + c_1 x + c_0 \]

donde numeramos los coeficientes \(c_k\) según la potencia \(k\) de cada término.

Entonces, para poder definir una función general evaluar_polinomio, necesitamos de 2 cosas:

  1. definir «variables numeradas»,

  2. sumar tantos términos como coeficientes que hayamos recibido.

Si pudiésemos definir variables numeradas, podríamos saber que la variable \(k\)-ésima corresponde al coeficiente del término \(k\)-ésimo.

Definir variables numeradas no es simplemente poner un número en el nombre de las variables. Por ejemplo,

def evaluar_polinomio(x, c_0, c_1):
    ...

es evaluar_lineal con otros nombres para sus parámetros, y solo acepta dos coeficientes.

Para poder hacer esto, vamos a introducir 3 herramientas nuevas:

  • la lista,

  • el bucle for,

  • y la función range.

Listas#

La lista es una estructura de datos que nos permite definir «variables numeradas».

Para definir una lista, ponemos los elementos entre corchetes, [], separados por comas.

Por ejemplo, definamos una lista con los primeros 5 números primos:

primos = [2, 3, 5, 7, 11]

primos
[2, 3, 5, 7, 11]

Para conocer el largo de una lista, podemos usar la función len:

len(primos)
5

Acceder a una variable numerada#

Para acceder a un elemento de la lista, se usa la notación lista[numero].

La lista numera sus elementos desde 0.

Por ejemplo, para acceder al primer elemento, usamos:

primos[0]
2

Al segundo elemento:

primos[1]
3

Si tratamos de acceder a un elemento que no existe, nos arroja un error:

primos[10]
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
Cell In[8], line 1
----> 1 primos[10]

IndexError: list index out of range

Reasignar, agregar y eliminar elementos#

Podemos cambiar un elemento al igual que cuando reasignamos una variable:

primos[0] = 42

primos
[42, 3, 5, 7, 11]

Pero si queremos agregar nuevos elementos, no podemos asignar a posiciones que aún no existen:

primos[5] = 13
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
Cell In[10], line 1
----> 1 primos[5] = 13

IndexError: list assignment index out of range

Para agregar nuevos elementos, tenemos que usar el método append, que los agrega al final:

primos.append(13)

primos
[42, 3, 5, 7, 11, 13]

Nota: los métodos son funciones, con una pequeña diferencia que no es relevante discutir ahora.

Ahora sí, en la posición 5 está el sexto número primo:

primos[5]
13

Para borrar un elemento, la sintaxis es igual a la de borrar una variable.

Borremos el primero, que no es un número primo:

del primos[0]

primos
[3, 5, 7, 11, 13]

Pero, noten que cambió el largo y las posiciones en la lista. El número 3, que antes estaba en la posición 1, ahora está en la posición 0:

primos[0]
3

Ejercicio 1#

Reescribir la función evaluar_cuadratica(x, a, b, c) como evaluar_cuadratica(x, c) donde c es una lista de coeficientes.

Evaluar el polinomio \(f(x) = 2 x^2 + 3 x + 5\) en \(x=7\).

# Escriba su solución aquí

Solución#

Hide code cell content
def evaluar_cuadratica(x, c):
    return c[0] + c[1] * x + c[2] * x**2


coef = [5, 3, 2]
evaluar_cuadratica(7, coef)
124

Este es un primer paso. pero la función todavía solo sirve para cuadráticas.

Si le damos el coeficiente del término cúbico, lo ignora:

evaluar_cuadratica(7, [5, 3, 2, 100])
124

Y si le pasamos una lineal, nos arroja un error:

evaluar_cuadratica(7, [5, 3])
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
Cell In[18], line 1
----> 1 evaluar_cuadratica(7, [5, 3])

Cell In[16], line 2, in evaluar_cuadratica(x, c)
      1 def evaluar_cuadratica(x, c):
----> 2     return c[0] + c[1] * x + c[2] * x**2

IndexError: list index out of range

Como solo hay dos coeficientes, cuando trata de acceder al tercer coeficiente, c[2], nos dice que el índice de la lista se fue del rango válido.

Iteraciones y bucle for#

En cada término del polinomio, necesitamos hacer una cuenta similar, c[k] * x**k para k de 0 a n.

Entonces, necesitamos una forma de repetir una operación (c[k] * x**k), cambiando una variable (k).

Empecemos con un caso más «visual»: imprimir los números 2, 3 y 5, cada uno en una linea distinta.

Podemos hacerlo manualmente:

print(2)
print(3)
print(5)
2
3
5

Lo que cambia en cada linea es el argumento de print.

Si en lugar de pasarle un número, le pasamos una variable, las lineas que usan print son todas iguales:

x = 2
print(x)

x = 3
print(x)

x = 5
print(x)
2
3
5

¡Empeoramos las cosas, ahora es más largo!

Pero, tenemos una forma de decirle a Python que repita el mismo código, variando la asignación x = ...:

for x in [2, 3, 5]:
    print(x)
2
3
5

Se lee como: «para cada x en la lista [2, 3, 5], hace print(x)».

Este código hace exactamente lo mismo al de la celda anterior.

Se imaginarán que es muy útil si tenemos que repetir algo muchas veces.

Podemos decirle a Python como se hace una vez y que lo repita para todos.

Sintaxis del bucle for#

La sintaxis en general es

for variable in variables:
    bloque_de_codigo

donde for e in son palabras claves de Python, y variables es algún objeto que sepa pasarnos sus elementos de a uno.

Por ejemplo, la lista sabe pasar sus elementos en orden.

Para cada elemento de variables, Python lo asigna en variable, y corre el bloque de código una vez.

Al igual que al definir una función, Python reconoce cual es el bloque de código a repetir porque está indentado.

Ejercicio 2#

Muchos problemas se pueden descomponer en iterar una misma operación múltiples veces.

Por ejemplo, cuando queremos sumar 4 números:

2 + 3 + 5 + 7
17

los vamos sumando de a dos. Primero 2 + 3, luego al resultado le sumamos 5 y finalmente 7:

(((2 + 3) + 5) + 7)
17

Es decir, siempre vamos sumando el siguiente número a la suma de los anteriores: total = total + siguiente_numero

Si queremos aplicar esta idea, solo nos falta algo: ¿qué pasa al principio, cuando siguiente_numero es el primer número? ¿Cuánto vale total?

Antes de sumar el primer número, el total es 0, ¿no?

Entonces, este algoritmo nos serviría para sumar una lista de números:

total = 0

numero = 2
total = total + numero

numero = 3
total = total + numero

numero = 5
total = total + numero

numero = 7
total = total + numero

total
17

que vemos que llegamos al mismo resultado.

Reescríbanlo con un for ... in ...:

numeros = [2, 3, 5, 7]

# Escriba su solución aquí

Solución#

Hide code cell content
numeros = [2, 3, 5, 7]

total = 0
for numero in numeros:
    total = total + numero

total
17

Ejercicio 3#

Reescribir la función evaluar_cuadratica(x, c) usando un bucle for para sumar los términos.

Evaluar el polinomio \(f(x) = 2 x^2 + 3 x + 5\) en \(x=7\).

Ayuda: se puede reescribir el polinomio como una sumatoria, $\( f(x) = c_0 x^0 + c_1 x^1 + c_2 x^2 = \sum_{k=0}^2 c_k x^k \)$ ¿Qué es lo que varía en cada término?

# Escriba su solución aquí

Solución#

Hide code cell content
def evaluar_cuadratica(x, c):
    y = 0
    for i in [0, 1, 2]:
        y = y + c[i] * x**i
    return y


evaluar_cuadratica(7, [5, 3, 2])
124

La función range#

Lo último que nos falta es que los índices que pusimos a mano,

for i in [0, 1, 2]:
    ...

vayan de 0 hasta el final de la lista.

Si queremos iterar sobre números desde 0 hasta un dado n, sin incluir a este último, podemos usar la función range:

for x in range(3):
    print(x)
0
1
2

que nos va pasando los números de a uno.

Esta función genera los números enteros del intervalo cerrado-abierto \([0, n)\).

Es útil que no incluya el último número, ya que podemos hacer range(len(lista)) para que genere los índices de la lista.

primos = [2, 3, 5]

for i in range(len(primos)):
    primo = primos[i]
    print(primo)
2
3
5

Aunque, si solo queríamos acceder a la posición \(i\)-ésima, y no usamos el índice i para nada más, no hace falta usar range:

primos = [2, 3, 5]

for primo in primos:
    print(primo)
2
3
5

La función range tiene otras dos variantes:

  1. podemos cambiar el número en el que inicia con range(a, b), que genera el intervalo \([a,b)\)

for x in range(2, 5):
    print(x)
2
3
4
  1. podemos decirle que dé saltos mayores a 1 entre número y número con range(a, b, salto)

for x in range(4, 9, 2):
    print(x)
4
6
8

Ejercicio 4#

Reescribir la función evaluar_cuadratica para un polinomio de grado \(n\).

Pruebe evaluarla con polinomios de distinto grado.

# Escriba su solución aquí

Solución#

Hide code cell content
def evaluar_polinomio(x, c):
    y = 0
    for i in range(len(c)):
        y = y + c[i] * x**i
    return y

Ahora sí, escribimos una función para evaluar un polinomio de grado \(n\).

Para chequear que esté bien, evaluemos para casos sepamos la respuesta.

Por ejemplo, potencias de 10. Es decir, \(x^n\) para \(x=10\)

Para \(f(x) = x^2\):

evaluar_polinomio(10, [0, 0, 1])
100

Para \(f(x) = x^3\):

evaluar_polinomio(10, [0, 0, 0, 1])
1000

Para \(f(x) = 1\):

evaluar_polinomio(10, [1])
1

¿Qué pasa si evaluamos en x=0?

En nuestra solución, estamos calculando x**0 para el término constante.

En matemática, \(0^0\) no está definido.

En computación, 0**0 da 1.

Pruébenlo.