Cap. 11 - Listas

Una lista es una colección ordenada de valores. Los valores que integran la lista se llaman elementos o items de la misma. Usaremos ambos términos indistintamente. Las listas son similares a los strings, que son colecciones ordenadas de caracteres, con la diferencia de que los elementos de una lista pueden ser de cualquier tipo. Las listas, los strings y otros colecciones que mantienen el orden de sus items son llamados secuencias.

11.1) Listas de valores

Hay muchas formas de crear una lista. La más simple es encerrar sus elementos entre corchetes ([ y ])

		ps = [10, 20, 30, 40]
		qs = ["spam", "bungee", "swallow"]

El primer ejemplo es una lista de 4 números. El segundo ejemplo es una lista de 3 strings. Los elementos de una lista no tienen por qué ser del mismo tipo. La siguiente lista contiene un string, un float, un entero y (sorprendentemente) otra lista:

		zs = ["hola", 2.0, 5, [10, 20]]

Una lista dentro de otra lista se dice que está anidada.

Finalmente, una lista sin elementos se llama una lista vacía y se denota [ ]

Ya hemos visto que podemos asignar listas como valores a variables o pasar listas como parámetros a funciones:

		>>> vocabulario = ["manzana", "queso", "perro"]
		>>> numeros = [17, 123]
		>>> una_lista_vacia = []
		>>> print(vocabulario, numeros, una_lista_vacia)
		["apple", "cheese", "dog"] [17, 123] []

11.2) Acceso a elementos

La sintaxis para acceder a los elementos de una lista es la misma que para acceder a caracteres de un string - el operador de indexación: [ ] (no confundir con una lista vacía). La expresión dentro de los corchetes indica el índice. Recordar que los índices comienzan en 0:

		>>> numeros[0]
		17

Cualquier expresión que evalúe a un entero puede utilizarse como índice:

		>>> numeros[9-8]
		5
		>>> numeros[1.0]
		Traceback (most recent call last):
		  File "", line 1, in 
		TypeError: list indices must be integers, not float

Si tratas de acceder o asignar un elemento que no existe, tendrás un error de tiempo de ejecución:

		>>> numeros[2]
		Traceback (most recent call last):
		  File "", line 1, in 
		IndexError: list index out of range

Es común usar una variable de loop como índice de lista:

		jinetes = ["guerra", "hambre", "peste", "muerte"]
		for i in [0, 1, 2, 3]:
		    print(horsemen[i])

En cada iteración del loop la variable i se utiliza como índice en la lista, imprimiendo el iésimo elemento. Este patrón de computación se llama recorrida de lista.

El ejemplo de arriba sólo utiliza el índice i para obtener el elemento de la lista, por lo cual se podría omitir y usar el modo en que el loop for directamente toma los items sin mencionar índices:

		jinetes = ["guerra", "hambre", "peste", "muerte"]
		for jinete in jinetes:
		    print(jinete)

11.3) Largo (length) de una lista

La función len devuelve el largo de una lista, que es igual a su cantidad de elementos. Si vas a utilizar un índice entero para acceder a la lista, es buena idea utilizar ese valor como límite superior del loop en vez de una constante. De ese modo, si cambia el tamaño de la lista no habrá que ir recorriendo el programa para modificar todos los loops, funcionarán correctamente para cualquier tamaño de lista:

		jinetes = ["guerra", "hambre", "peste", "muerte"]
		for i in range(len(horsemen)):
		    print(horsemen[i])

La última vez que se ejecuta el body del loop, i vale len(horsemen) - 1, que es el índice del último elemento. Pero como antes, sigue siendo mejor utilizar la versión sin el índice que vimos al final de la sección 11.2, la cual funcionará para cualquier tamaño de lista!

Si bien una lista puede incluir otra lista, la lista anidada cuenta como un solo elemento en su lista madre. El largo de la lista siguiente es 4:

		>>> len(["fabricantes de automóviles", 1, ["Ford", "Toyota", "BMW"], [1, 2, 3]])
		4

11.4) Membresía de una lista

Los operadores booleanos in y not in sirven para chequear membresía en una secuencia. Ya los habíamos usado con strings, pero también funcionan con listas y con otras secuencias:

>>> jinetes = ["guerra", "hambre", "peste", "muerte"]
>>> "peste" in jinetes
True
>>> "libertinaje" in jinetes
False
>>> "libertinaje" not in jinetes
True

El uso de estos operadores permite mejorar el programa de loops anidados que habíamos implementado en la sección 7.22, loops anidados para datos anidados. (estaba en L7_suma_de_numeros.py - función print_estudiantes - la versión mejorada está en L11_listas - función print_estudiantes)

    estudiantes = [
        ("Juan", ["Informática", "Física"]),
        ("Dani", ["Matemáticas", "Informática", "Estadística"]),
        ("Jess", ["Informática", "Contabilidad", "Economía", "Administración"]),
        ("Sarah", ["Informática", "Contabilidad", "Economía", "Filosofía"]),
        ("Zaira", ["Sociología", "Economía", "Derecho", "Estadísticas", "Música"])]

    # Contar cuántos estudiantes estudian Informática
    counter = 0
    for (nombre, cursos) in estudiantes:
        if "Informática" in cursos:        # Ya no hay loop anidado!
            counter += 1

    print("El número de estudiantes que cursan Informática es", counter)

11.5) Operaciones con listas

El operador + concatena listas:

		>>> a = [1, 2, 3]
		>>> b = [4, 5, 6]
		>>> c = a + b
		>>> c
		[1, 2, 3, 4, 5, 6]

El operador * repite una lista cierta cantidad de veces:

		>>> [0] * 4
		[0, 0, 0, 0]
		>>> [1, 2, 3] * 3
		[1, 2, 3, 1, 2, 3, 1, 2, 3]

El primer ejemplo repite a la lista [0] cuatro veces. El segundo ejemplo repite a la lista [1, 2, 3] tres veces.

11.6) Trozos de listas

Los operadores de trozos (slices) que habíamos visto para strings también nos permiten trabajar con sublistas:

>>> a_lista = ["a", "b", "c", "d", "e", "f"]
>>> a_lista[1:3]
['b', 'c']
>>> a_lista[:4]
['a', 'b', 'c', 'd']
>>> a_lista[3:]
['d', 'e', 'f']
>>> a_lista[:]
['a', 'b', 'c', 'd', 'e', 'f']

11.7) Las listas pueden cambiar

A diferencia de los strings, las listas son mutables, lo que significa que podemos cambiar sus elementos. Usando el operador de indexación en el lado izquierdo de la asignación, podemos actualizar uno de sus elementos:

		>>> frutas = ["banana", "manzana", "ananá"]
		>>> frutas[0] = "pera"
		>>> frutas[2] = "naranja"
		>>> frutas
		['pera', 'manzana', 'naranja']

El operador [ ] aplicado a una lista puede aparecer en cualquier parte de una expresión. Si aparece a la izquierda de una asignación, permite modificar uno de los elementos de la lista, por lo cual en el código anterior el primer elemento de la lista cambia de "banana" a "pera" y el último cambia de "ananá" a "naranja". La asignación de un elemento de la lista se llama asignación de item (acción que no está permitida para strings).

		>>> mi_string = "TEST"
		>>> mi_string[2] = "X"
		Traceback (most recent call last):
		  File "", line 1, in 
		TypeError: 'str' object does not support item assignment

Pero funciona para listas:

		>>> mi_lista = ["T", "E", "S", "T"]
		>>> mi_lista[2] = "X"
		>>> mi_lista
		['T', 'E', 'X', 'T']

Con el operador slice podemos actualizar una sublista completa de un golpe:

		>>> una_lista = ["a", "b", "c", "d", "e", "f"]
		>>> una_lista[1:3] = ["x", "y"]
		>>> una_lista
		['a', 'x', 'y', 'd', 'e', 'f']

Podemos incluso eliminar elementos de una lista asignando una lista vacía a ellos:

		>>> una_lista = ["a", "b", "c", "d", "e", "f"]
		>>> una_lista[1:3] = []
		>>> una_lista
		['a', 'd', 'e', 'f']

Y podemos agregar elementos en una lista estrujándolos en un trozo vacío en la posición que deseemos:

		>>> una_lista = ["a", "d", "f"]
		>>> una_lista[1:1] = ["b", "c"]
		>>> una_lista
		['a', 'b', 'c', 'd', 'f']
		>>> una_lista[4:4] = ["e"]
		>>> una_lista
		['a', 'b', 'c', 'd', 'e', 'f']

11.8) Borrando de una lista

Usar slices para borrar de una lista puede conducir a errores. Python provee una alternativa que es más legible. La sentencia del remueve un elemento de una lista:

		>>> a = ["uno", "dos", "tres"]
		>>> del a[1]
		>>> a
		['uno', 'tres']

Como puedes esperar, del causa un error de tiempo de ejecución si el índice está fuera de rango.

También puedes usar del con un operador slice para borrar una sublista:

		>>> una_lista = ["a", "b", "c", "d", "e", "f"]
		>>> del una_lista[1:5]
		>>> una_lista
		['a', 'f']

Observación: Sería interesante detenerse a comparar ambos modos de eliminar elementos de una lista. ¿Este es más seguro? ¿Por qué? ¿El otro es más expresivo o poderoso? Pensar en casos concretos.

11.9) Objetos y referencias

Después de ejecutar estas asignaciones:

		a = "banana"
		b = "banana"

Sabemos que a y b se van a referir a un objeto string con las letras "banana". Pero a priori no sabemos si apuntan o no al mismo objeto de tipo string. (esto no ocurriría en casi ningún otro lenguaje y es un punto débil de Python!)

Hay dos posibles formas en que el intérprete de Python los puede guardar en memoria:

En el primer caso, a y b se refieren a dos objetos distintos que tienen el mismo valor. En el segundo caso, se refieren al mismo objeto.

Podemos chequear si dos nombres se refieren al mismo objeto mediante el operador is:

		>>> a is b
		True

Esto nos dice que a y b se refieren al mismo objeto, es decir que en este caso se está dando la segunda de las dos posibilidades descritas arriba.

Como los strings son inmutables, Python optimiza recursos haciendo que dos nombres que se refieren al mismo valor de string se refieran también al mismo objeto.

Esto no puede ocurrir con las listas:

		>>> a = [1, 2, 3]
		>>> b = [1, 2, 3]
		>>> a == b
		True
		>>> a is b
		False

Tratándose de listas, a y b tienen el mismo valor pero no se refieren al mismo objeto. El snapshot del estado interno para listas siempre se verá así:

11.10) Alias de listas

Ya que las variables se refieren a objetos, al asignar una variable a otra, ambas variables se refieren al mismo objeto:

		>>> a = [1, 2, 3]
		>>> b = a
		>>> a is b
		True

En este caso, el snapshot se vería así:

Dado que se trata de la misma lista con dos nombres distintos, a y b, decimos que ambas variables son alias. Los cambios realizados en un alias afectarán al otro:

		>>> b[0] = 5
		>>> a
		[5, 2, 3]

a = 5	b = a	a = 7				b sigue valiendo 5
a = "hola"	b = a	a = "chau"		b sigue valiendo "hola"
a = [0, 1, 2]	b = a	a = [3, 4, 5]		b sigue valiendo [0, 1, 2]

Si bien este comportamiento puede ser útil, en ocasiones es inesperado o indeseable.

11.11) Clonación de listas

Si queremos modificar una lista y al mismo tiempo mantener una copia de la original, necesitamos hacer una copia de la lista misma, no sólo de su referencia. Este proceso se conoce como clonación, para evitar la ambigüedad de la palabra copia. (pues "copia" puede referirse tanto a un alias como a un clon, o sea a una copia por referencia como a una copia por valor)

La forma más fácil de clonar una lista es mediante el operador slice:

		>>> a = [1, 2, 3]
		>>> b = a[:]
		>>> b
		[1, 2, 3]

Al tomar cualquier trozo de a estamos creando una nueva lista. En este caso el trozo consiste en la lista completa, así que la nueva relación es así:

Ahora podemos modificar a b libremente sin riesgo de cambiar algo en a por accidente.

		>>> b[0] = 5
		>>> a
		[1, 2, 3]

11.12) Listas y loops FOR

El loop for también funciona con listas, como ya hemos visto. La sintaxis general de un loop for es:

		for VARIABLE in LISTA:
			CUERPO

Por ejemplo, ya habíamos visto un caso así:

		amigos = ["Joe", "Zoe", "Brad", "Angelina", "Zuki", "Thandi", "Paris"]
		for amigo in amigos:
		    print(amigo)

Casi se lee como en inglés: Por (cada) amigo en (la lista de) amigos, imprimir (el nombre de) amigo.

Cualquier forma en que se pueda expresar una lista puede ser utilizada en el loop for:

		for numero in range(20):
		    if numero % 3 == 0:
		        print(numero)

		for fruta in ["banana", "manzana", "membrillo"]:
		    print("Me gusta comer " + fruta + "s!")

El primer ejemplo imprime todo los múltiplos de 3 entre 0 y 19. El segundo ejemplo expresa nuestro entusiasmo por diversas frutas.

Dado que las listas son mutables, muchas veces querremos recorrerlas cambiando sus elementos. El siguiente código eleva al cuadrado todos los elementos de xs:

		xs = [1, 2, 3, 4, 5]

		for i in range(len(xs)):
		    xs[i] = xs[i]**2

Tómate un tiempo para pensar qué hace range(len(xs)).

En este ejemplo nos interesa tanto el valor de un item (queremos elevar ese valor al cuadrado) como su índice (para que podamos asignar el nuevo valor a dicha posición). Esta necesidad es tan habitual que Python provee de una forma más cómoda y directa para implementarlo:

		xs = [1, 2, 3, 4, 5]

		for (i, val) in enumerate(xs):
		    xs[i] = val**2

La función enumerate genera pares de ambos (índice, valor) durante la recorrida de la lista. Prueba el siguiente ejemplo para comprender mejor cómo funciona enumerate:

		for (i, v) in enumerate(["banana", "manzana", "pera", "limón"]):
		     print(i, v)

El output será:

		0 banana
		1 manzana
		2 pera
		3 limón

Observación: Se puede usar la función enumerate con un string:

		for (i, v) in enumerate("hola qué tal"):
		     print(i, v)

0 h
1 o
2 l
3 a
4  
5 q
6 u
7 é
8  
9 t
10 a
11 l		

11.13) Listas como parámetros

Cuando pasamos una lista como argumento lo que pasamos es una referencia a la lista, no un clon de la misma. Por lo tanto el paso de parámetros crea un alias: el llamador tiene una variable que referencia a la lista, y la función llamada tiene un alias, pero hay un único objeto de lista subyacente. Por ejemplo, la función siguiente toma una lista como argumento y multiplica cada elemento en la lista por 2:

		def duplicar_elementos(lista):
		    """ Sustituir cada elemento en lista con el doble de su valor. """
		    for (idx, val) in enumerate(lista):
		        lista[idx] = 2 * val

Si entonces ejecutamos estas líneas:

		cosas = [2, 5, 9]
		duplicar_elementos(cosas)
		print(cosas)

Resultará que cosas fue modificado en la función y entonces el output es:

		[4, 10, 18]

En la función duplicar_elementos, el parámetro lista y la variable cosas (pasada como argumento) son alias del mismo objeto. Así que antes de cualquier cambio a los elementos de la lista, el state snapshot se ve así:

Como el objeto lista es compartido por dos marcos, lo representamos compartido (en el diagrama).

Si la función modifica los items del parámetro, el llamador ve el cambio. (la figura anterior tiene 2 problemas: (1) que está en inglés; (2) que en la versión original en inglés están invertidas las relaciones: __main__ debería ir con things y double_stuff debería ir con a_list)

Utiliza el visualizador de Python!

  • Te recomendamos el visualizador de Python que está disponible en PythonTutor.com.
  • Es una herramienta muy útil para adquirir una buena comprensión sobre las referencias, alias, asignaciones y pasajes de argumentos a funciones. Presta especial atención a los casos en que clonas una lista o tienes dos listas separadas, y los casos en que sólo hay una lista subyacente, pero más de una variable como alias de la misma lista.

11.14) Métodos de listas

El operador punto puede usarse también para acceder a métodos built-in del objeto lista. Comenzaremos con el método más útil para agregar algo al final de una lista existente:

>>> mi_lista = []
>>> mi_lista.append(5)
>>> mi_lista.append(27)
>>> mi_lista.append(3)
>>> mi_lista.append(12)
>>> mi_lista
[5, 27, 3, 12]

El método append agrega el argumento que se le pase al final de la lista. Lo usaremos intensamente cuando creemos nuevas listas. Veamos ejemplos de uso de otros métodos de lista:

>>> mi_lista.insert(1, 12)  		# Inserta 12 en la posición 1, y corre los elementos siguientes un lugar
>>> mi_lista
[5, 12, 27, 3, 12]
>>> mi_lista.count(12)       		# Cuenta cuántas veces aparece 12 en la lista
2
>>> mi_lista.extend([5, 9, 5, 11])   	# Concatena una lista al final de la otra
>>> mi_lista
[5, 12, 27, 3, 12, 5, 9, 5, 11]
>>> mi_lista.index(9)                	# Encuentra el índice de la primer aparición de 9 en la lista - devuelve error si el valor que se le pasa no está en la lista
6
>>> mi_lista.reverse()
>>> mi_lista
[11, 5, 9, 5, 12, 3, 27, 12, 5]
>>> mi_lista.sort()			# Ordena la lista
>>> mi_lista
[3, 5, 5, 5, 9, 11, 12, 12, 27]
>>> mi_lista.remove(12)             	# Elimina el primer 12 que se encuentre en la lista (no elimina a todos) - devuelve error si el valor que se le pasa no está en la lista
>>> mylist
[3, 5, 5, 5, 9, 11, 12, 27]

Es recomendable que experimentes con los métodos de lista mostrados aquí, y leas su documentación hasta que tengas confianza en que comprendes cómo funcionan.

11.15) Funciones puras y modificadores

Las funciones que reciben listas como argumentos y las modifican durante la ejecución se llaman modificadores y los cambios que realizan se llaman efectos laterales.

Una función pura no produce efectos laterales. Sólo se comunica con el llamador a través de los parámetros, a los cuales no modifica, y un valor de retorno. Esta es la función duplicar_elementos escrita como una función pura:

def duplicar_elementos_(lista):
    """ Devuelve una nueva lista que contiene los elementos duplicados de lista. """
    nueva_lista = []
    for valor in lista:
        nuevo_elem = 2 * valor
        nueva_lista.append(nuevo_elem)

    return nueva_lista

Esta versión de duplicar_elementos_ no modifica sus argumentos:

>>> cosas = [2, 5, 9]
>>> xs = duplicar_elementos(cosas)
>>> cosas
[2, 5, 9]
>>> xs
[4, 10, 18]

Una regla que vimos hace tiempo dice que para las asignaciones "primero se evalúa el lado derecho, y luego se asigna el resultado a la variable de la izquierda". Por lo tanto, no hay riesgo en asignar el resultado de la función a la misma variable que se pasó a la función:

>>> cosas = [2, 5, 9]
>>> xs = duplicar_elementos(cosas)
>>> cosas
[4, 10, 18]

Qué estilo es mejor?

Aclaración: La expresión "programación funcional" se usa en informática con un sentido distinto y más preciso que el genérico que se utiliza en este curso. Sin embargo usaremos la expresión "programación funcional" con el sentido más general que se le da en este curso, es decir, programar basándose en funciones puras sin utilizar modificadores.

11.16) Funciones que producen listas

La versión pura de duplicar_elementos_ que vimos en la sección anterior utilizó un patrón importante para tu caja de herramientas. Siempre que necesites escribir una función que cree y retorne una lista, el patrón va a ser algo así:

		inicializar una variable de resultado como una lista vacía
		loop
		    crear un nuevo elemento
		    agregarlo al final de la lista de resultados
		return del resultado

Veamos otro uso de este patrón. Supongamos que ya tenemos una función es_primo(x) que puede chequear si x es primo. Escribe una función que retorne una lista con todos los números primos hasta n: (no implementada en archivo porque no tengo la función es_primo - sería interesante implementar a ambas)

		def primos_menores_que(n):
		    """ Retorna una lista con todos los primeros menores que n. """
		    resultado = []
		    for i in range(2, n):
		        if is_prime(i):
		           resultado.append(i)
		    return resultado

11.17) Strings y listas

Dos de los métodos más útiles para strings tienen que ver con la conversión desde y hacia listas de substrings. El método split (que ya hemos visto) rompe un string en una lista de palabras. Por defecto, todo carácter o conjunto consecutivo de caracteres en blanco es considerado como un borde de palabra:

		>>> frase = "Pablito clavó un clavito..."
		>>> palabras = frase.split()
		>>> palabras
		['Pablito', 'clavó', 'un', 'clavito...']

Un argumento opcional llamado delimitador puede ser usado para especificar qué string hay que usar como delimitador de borde entre substrings. El siguiente ejemplo usa el string 'la' como delimitador:

		>>> frase = "Pablito clavó un clavito..."
		>>> palabras = frase.split('la')
		>>> palabras
		['Pablito c', 'vó un c', 'vito...']

Observar que el delimitador no aparece en el resultado.

El método inverso a split es join. Se elige un delimitador (separador) deseado (habitualmente conocido como el pegamento) y se une la lista con el pegamento colocado entre elemento y elemento.

		>>> glue = ";"
		>>> s = glue.join(palabras)
		>>> s
		'Pablito;clavó;un;clavito...'

La lista que se pega (en este ejemplo, palabras) no es modificada. También, como se ve en los ejemplos sigiuentes, se puede utilizar un pegamento vacío o compueto por múltiples caracteres:

		>>> " --- ".join(palabras)
		'Pablito --- clavó --- un --- clavito...'
		>>> "".join(palabras)
		'Pablitoclavóunclavito...'

11.18) LIST y RANGE

Python tiene una función built-in de conversión de tipos list, que trata de convertir lo que sea que le pases en una lista:

		>>> xs = list("Rana suculenta")
		>>> xs
		["R", "a", "n", "a", " ", "s", "u", "c", "u", "l", "e", "n", "t", "a"]
		>>> "".join(xs)
		'Rana suculenta'

Una característica particular de range es que no computa todos sus valores instantáneamente. Pone "en off" la computación y la va haciendo bajo demanda, o "perezosamente". Podemos decir que devuelve la promesa de producir los valores a medida que sean necesarios. Esto es muy conveniente si tu programa corto-circuita una búsqueda y retorna temprano, como en este ejemplo:

def f(n):
    """ Encontrar el primer entero positivo entre 101 y n que sea divisible por 21
    """
    for i in range(101, n):
       if (i % 21 == 0):
           return i

test(f(110) == 105)
test(f(1000000000) == 105)

En el segundo test, si range fuera a construir prematuramente la lista de todos los elementos que promete retornar, rápidamente agotaría la memoria y colgaría al programa.

A veces encontrarás al perezoso range metido dentro del llamado a una lista. Esto obliga a Python a convertir la llamada perezosa en una lista completa:

>>> range(10)           # Crea una promesa perezosa
range(0, 10)
>>> list(range(10))     # Llama a toda la promesa para forzar la lista completa
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

11.19) Listas anidadas

Una lista anidada es una lista que aparece como elemento de otra lista. En la siguiente lista, el elemento cuyo índice es 3 es otra lista:

		>>> lista_principal = ["hola", 2.0, 5, [10, 20]]

Si hacemos un output del elemento en el índice 3, obtenemos como resultado una lista:

		>>> print(lista_principal[3])
		[10, 20]

Para extraer un elemento de la lista anidada, podemos proceder en 2 pasos:

		>>> lista_anidada = lista_principal[3]
		>>> lista_anidada[0]
		10

O bien podemos combinar ambas extracciones en un paso:

		>>> lista_principal[3][1]
		20

Los operadores [] (corchete) operan de izquierda a derecha, así que la operación obtiene el elemento de índice 3 de lista_principal (que es la lista anidada) y extrae el elemento de índice 1 de esta lista (que es el número 20).

11.20) Matrices

Las listas anidadas se usan con frecuencia para representar matrices. Por ejemplo, la matriz:

Puede representarse como:

		>>> mz = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

Aquí mz es una lista con 3 elementos, en que cada elemento es una fila de la matriz. Podemos seleccionar una fila completa de la matriz con el procedimiento habitual:

		>>> mx[1]
		[4, 5, 6]

O podemos extraer un elemento de la matriz usando la fórmula del doble-índice:

		>>> mx[1][2]
		6

El primer índice selecciona la fila, y el segundo índice selecciona la columna. A pesar de que esta forma de representar matrices es común, no es la única posibilidad. Una variante obvia (también válida) es utilizar una lista de columnas en vez de una lista de filas. Y más adelante veremos una alternativa aun más radical utilizando diccionarios.

11.21) Glosario

11.22) Ejercicios

1) Cuál sería la respuesta del intérprete de Python a lo siguiente?

		>>> list(range(10, 0, -2))

 

2) Considera este fragmento de código:

		import turtle

		tess = turtle.Turtle()
		alex = tess
		alex.color("hotpink")

 

3) Dibujar un snapshot de estados para a y b antes y después de la tercera línea del siguiente código Python:

		a = [1, 2, 3]
		b = a[:]
		b[0] = 5

 

4) Cuál será el output del programa siguiente?

		this = ["Yo", "no", "soy", "un", "monstruo"]
		that = ["Yo", "no", "soy", "un", "monstruo"]
		print("Test 1: {0}".format(this is that))
		that = this
		print("Test 2: {0}".format(this is that))

 

5) Las listas pueden usarse para representar vectores matemáticos. En este ejercicio y algunos de los que siguen escribirás funciones para realizar operaciones estándar sobre vectores.

		test(suma_vectores([1, 1], [1, 1]) == [2, 2])
		test(suma_vectores([1, 2], [1, 4]) == [2, 6])
		test(suma_vectores([1, 2, 1], [1, 4, 3]) == [2, 6, 4])

 

6) Escribe una función producto_por_escalar(s, v) que reciba un número s y una lista v y devuelva el producto de s por v: (hacerlo)

		test(producto_por_escalar(5, [1, 2]) == [5, 10])
		test(producto_por_escalar(3, [1, 0, -1]) == [3, 0, -3])
		test(producto_por_escalar(7, [3, 0, 5, 11, 2]) == [21, 0, 35, 77, 14])

 

7) Escribe una función producto_escalar(u, v) que reciba dos listas de números del mismo tamaño, y devuelva la suma de los productos de los elementos corresponddientes de cada uno (producto escalar o producto punto de vectores). (hacerlo)

		test(producto_escalar([1, 1], [1, 1]) ==  2)
		test(producto_escalar([1, 2], [1, 4]) ==  9)
		test(producto_escalar([1, 2, 1], [1, 4, 3]) == 12)

 

8) Desafío extra para los amantes de la matemática: Escribe una función producto_vectorial(u, v) que tome dos listas de números de largo 3 y devuelva su producto vectorial. (hacerlo)

 

9) Describe en qué se parecen y en qué se diferencian frase y " ".join(frase.split()) en el fragmento de código siguiente. ¿Son iguales para todos los strings que asignemos a frase? ¿Cuándo serían diferentes?

		frase = "Pablito clavó un clavito..."

 

10) Escribe una función replace(s, old, new) que sustituya las ocurrencias de old con new en el string s (hacerlo)

		test(replace("Mississippi", "i", "I") == "MIssIssIppI")

		s = "I love spom! Spom is my favorite food. Spom, spom, yum!"
		test(replace(s, "om", "am") ==
		    "I love spam! Spam is my favorite food. Spam, spam, yum!")

		test(replace(s, "o", "a") ==
		    "I lave spam! Spam is my favarite faad. Spam, spam, yum!")

 

11) Supón que quieres intercambiar los valores entre dos variables. Decides implementarlo como una función reutilizable, y escribes este código:

		def swap_incorrecto(x, y):      	# Versión incorrecta del swap
		    print("2) Antes de la inversión: x:", x, "y:", y)
		    (x, y) = (y, x)
		    print("3) Después de la inversión: x:", x, "y:", y)

		a = ["Qué", "está", "pasando?"]
		b = [2,3,4]
		#print("1) Antes de llamar a la función: a:", a, "b:", b)
		#swap_incorrecto(a, b)
		#print("4) Después de llamar a la función: a:", a, "b:", b)