Herramientas Informaticas

Autor: juliocesar20200413 Página 114 de 141

Desarrollador web apasionado y gusto por la buena musica

8.15. Matrices

Es común usar listas anidadas para representar matrices. Por ejemplo, la matriz:

1 2 3
7 8 9
4 5 6

puede ser representada como:

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

matriz es una lista con tres elementos, siendo cada elemento una fila de la matriz. Podemos elegir una fila entera de la matriz de la forma normal:

   1: >>> matriz[1]

   2: [4, 5, 6]

O tomar solo un elemento de la matriz usando la forma de doble índice:

   1: >>> matriz[1][1]

   2: 5

El primer índice escoge la fila y el segundo la columna. Aunque esta manera de representar matrices es común, no es la única posibilidad. Una pequeña variación consiste en usar una lista de columnas en lugar de filas. Mas adelante veremos una alternativa mas radical usando un diccionario.

8.16. Cadenas y listas

Dos de las funciones mas útiles del modulo string tienen que ver con listas de cadenas. La función split divide una cadena en una lista de palabras. Por defecto, cualquier numero de caracteres de espacio en blanco se considera un límite de palabra:

   1: >>> import string

   2: >>> cancion = "La lluvia en Sevilla..."

   3: >>> string.split(cancion)

   4: ['La', 'lluvia', 'en', 'Sevilla...']

Se puede usar un argumento opcional llamado delimitador para especificar que caracteres se usaran como límites de palabra. El siguiente ejemplo usa la cadena ll como delimitador:

   1: >>> string.split(cancion, 'll')

   2: ['La ', 'uvia en Sevi', 'a...']

Observe que el delimitador no aparece en la lista.

La función join es la inversa de split. Toma una lista de cadenas y concatena

los elementos con un espacio entre cada par:

   1: >>> lista = ['La', 'lluvia', 'en', 'Sevilla...']

   2: >>> string.join(lista)

   3: 'La lluvia en Sevilla...'

Como split, join acepta un delimitador opcional que se inserta entre los elementos. El delimitador por defecto es el espacio.

   1: >>> string.join(lista, '_')

   2: 'La_lluvia_en_Sevilla...'

A modo de ejercicio, describa la relación que hay entre
string.join(string.split(cancion)) y canción. ¿Esla misma para todas las cadenas?
¿Cuando sería diferente?

8.17. Glosario

lista: Una colección de objetos con nombre, en la que cada objeto es identificado por un índice.

índice: Una variable o valor enteros que se usan para indicar un elemento de una lista.

elemento: Uno de los valores de una lista (u otra secuencia). El operador corchete selecciona elementos de una lista.

secuencia: Cualquier tipo de datos que consista en un conjunto ordenado de elementos, con cada elemento identificado por un índice.

lista anidada: Una lista que es elemento de otra lista.

recorrido de lista: Acceso secuencial a cada elemento de una lista.

objeto: Una cosa a la que se puede referir una variable.

alias: Múltiples variables que contienen referencias al mismo objeto.

clonar: Crear un objeto nuevo que tiene el mismo valor que un objeto ya existente. Copiar una referencia a un objeto crea un alias, pero no clona el objeto.

delimitador: Un carácter o cadena utilizado para indicar donde debe cortarse una cadena.

Capítulo 9

Tuplas

9.1. Mutabilidad y tuplas

Hasta ahora, ha visto dos tipos compuestos: cadenas, que están hechas de caracteres, y listas, que están hechas de elementos de cualquier tipo. Una de las diferencias que señalamos es que los elementos de una lista se pueden modificar, pero los caracteres de una cadena no. En otras palabras, las cadenas son inmutables y las listas son mutables.

En Python hay otro tipo llamado tupla que es similar a una lista salvo en que es inmutable. Sintacticamente, una tupla es una lista de valores separados por comas:

   1: >>> tupla = 'a', 'b', 'c', 'd', 'e'

Aunque no es necesario, la convención dice que hay que encerrar las tuplas entre paréntesis:

   1: >>> tupla = ('a', 'b', 'c', 'd', 'e')

Para crear una tupla con un solo elemento, debemos incluir una coma final:

   1: >>> t1 = ('a',)

   2: >>> type(t1)

   3: <type 'tuple'>

Sin la coma, Python trata (á’) como una cadena entre paréntesis:

   1: >>> t2 = ('a')

   2: >>> type(t2)

   3: <type 'string'>

Dejando a un lado las cuestiones de sintaxis, las operaciones sobre las tuplas son las mismas que sobre las listas. El operador índice selecciona un elemento de la tupla.

   1: >>> tupla = ('a', 'b', 'c', 'd', 'e')

   2: >>> tupla[0]

   3: 'a'

Y el operador de porción selecciona un intervalo de elementos.

   1: >>> tupla[1:3]

   2: ('b', 'c')

Pero si intentamos modificar uno de los elementos de la tupla provocaremos un error:

   1: >>> tupla[0] = 'A'

   2: TypeError: object doesn't support item assignment

Por supuesto, incluso aunque no podamos modificar los elementos de una tupla, podemos sustituir una tupla por otra diferente:

   1: >>> tupla = ('A',) + tupla[1:]

   2: >>> tupla

   3: ('A', 'b', 'c', 'd', 'e')

9.2. Asignación de tuplas

De vez en cuando, es útil intercambiar los valores de dos variables. Para hacerlo con sentencias de asignación convencionales debemos usar una variable temporal. Por ejemplo, para intercambiar a y b:

   1: >>> temp = a

   2: >>> a = b

   3: >>> b = temp

Si tenemos que hacer esto a menudo, esta aproximación resulta aparatosa. Python proporciona una forma de asignación de tuplas que soluciona este problema elegantemente:

   1: >>> a, b = b, a

El lado izquierdo es una tupla de variables, el lado derecho es una tupla de valores. Cada valor se asigna a su respectiva variable. Todas las expresiones del lado derecho se evalúan antes de las asignaciones. Esta característica hace de la asignación de tuplas algo muy versátil.

Naturalmente, el número de variables a la izquierda y el número de valores a la derecha deben ser iguales:

   1: >>> a, b, c, d = 1, 2, 3

   2: ValueError: unpack tuple of wrong size

9.3. Tuplas como valor de retorno

Las funciones pueden devolver tuplas como valor de retorno. Por ejemplo, podría escribir una función que intercambie dos parámetros:

   1: def intercambio(x, y):

   2:     return y, x

Luego podemos asignar el valor de retorno a una tupla con dos variables:

   1: a, b = intercambio(a, b)

En este caso, no hay ninguna ventaja en convertir intercambio en una función.
De hecho, existe un peligro al intentar encapsular intercambio, y es el tentador error que sigue:

   1: def intercambio(x, y): # versión incorrecta

   2: x, y = y, x

Si llamamos a esta función así:

   1: intercambio(a, b)

a y x son alias del mismo valor. Cambiar x dentro de intercambio hace que x se refiera a un valor diferente, pero no tiene efecto alguno sobre a en main .

De forma similar, cambiar y no tiene efecto sobre b.

Esta función se ejecuta sin generar un mensaje de error, pero no hace lo que intentamos. Este es un ejemplo de error semántico.

A modo de ejercicio, dibuje un diagrama de estados para esta función de manera que pueda ver por que no trabaja como usted quiere.

9.4. Números aleatorios

La mayor parte de los programas hacen lo mismo cada vez que los ejecutamos, por lo que se dice que son deterministas.

Normalmente el determinismo es una cosa buena, ya que esperamos que un calculo nos de siempre el mismo resultado.

Para algunas aplicaciones, sin embargo, queremos que el computador sea impredecible. El ejemplo obvio son los juegos, pero hay mas.

Hacer que un programa sea realmente no determinista resulta no ser tan sencillo, pero hay formas de que al menos parezca no determinista. Una de ellas es generar números aleatorios y usarlos para determinar el resultado del programa.

Python proporciona una función interna que genera números pseudoaleatorios, que no son verdaderamente aleatorios en un sentido matemático, pero servirán para nuestros propósitos.

El modulo random contiene una función llamada random que devuelve un numero en coma flotante entre 0,0 y 1,0. Cada vez que usted llama a random obtiene el siguiente numero de una larga serie. Para ver un ejemplo, ejecute este bucle:

   1: import random

   2:  

   3: for i in range(10):

   4:     x = random.random()

   5:     print x

Para generar un numero aleatorio entre 0,0 y un límite superior como maximo, multiplique x por maximo.

Como ejercicio, genere un numero aleatorio entre minimo y maximo.
Como ejercicio adicional, genere un numero aleatorio entero entre minimo y maximo, incluyendo ambos extremos.

9.5. Lista de números aleatorios

El primer paso es generar una lista de valores aleatorios. listaAleatorios acepta un parámetro entero y devuelve una lista de números aleatorios de la longitud dada. Comienza con una lista de n ceros. Cada vez que ejecuta el bucle, sustituye uno de los elementos con un numero aleatorio. El valor de retorno es una referencia a la lista completa:

   1: def listaAleatorios(n):

   2: s = [0] * n

   3: for i in range(n):

   4: s[i] = random.random()

   5: return s

Vamos a probar esta función con una lista de ocho elementos. A la hora de depurar es una buena idea empezar con algo pequeño.

   1: >>> listaAleatorios(8)

   2: 0.15156642489

   3: 0.498048560109

   4: 0.810894847068

   5: 0.360371157682

   6: 0.275119183077

   7: 0.328578797631

   8: 0.759199803101

   9: 0.800367163582

Se supone que los números generados por random están distribuidos uniformemente, lo que significa que cada valor es igualmente probable.

Si dividimos el intervalo de valores posibles en “baldes” de igual tamaño y contamos el numero de veces que un valor cae en cada balde, deberemos tener mas o menos el mismo numero en todos.

Podemos contrastar esta teor³a escribiendo un programa que divida el intervalo en baldes y contando el numero de valores en cada uno.

9.6. Conteo

Un buen enfoque sobre problemas como este es dividir el problema en subproblemas que encajen en un esquema computacional que hayamos visto antes.

En este caso, queremos recorrer una lista de números y contar el numero de veces que un valor cae en un intervalo dado.

Eso nos suena. En la Sección 7.8 escribimos un programa que recorr³a una cadena de texto y contaba el numero
de veces que aparecía una letra determinada.

Así, podemos hacerlo copiando el programa viejo y adaptándolo al problema actual. El programa original era:

   1: cuenta = 0

   2: for car in fruta:

   3:     if car == 'a':

   4:         cuenta = cuenta + 1

   5: print cuenta

El primer paso es sustituir fruta con lista y car con núm. Esto no cambia el programa, solo lo hace mas legible.

El segundo paso es cambiar la comprobación. No estamos interesados en encontrar letras. Queremos ver si num esta entre los valores de mínimo y máximo.

   1: cuenta = 0

   2: for num in lista

   3:     if minimo < num < maximo:

   4:         cuenta = cuenta + 1

   5: print cuenta

El ultimo paso es encapsular este código en una función llamada enElBalde.

Los parámetros son la lista y los valores minimo y maximo.

   1: def enElBalde(lista, minimo, maximo):

   2:     cuenta = 0

   3:     for num in lista:

   4:             if minimo < num < maximo:

   5:                 cuenta = cuenta + 1

   6:     return cuenta

Copiar y modificar un programa existente nos facilita escribir esta función rápidamente y nos ahorra un montón de tiempo de depuración. Este plan de desarrollo se llama coincidencia de esquemas. Si se encuentra trabajando en un
problema que ya soluciono, reutilice la solución.

9.7. Muchos baldes

Tal como aumenta el numero de baldes, enElBalde se hace un tanto difícil de manejar. Con dos baldes, no esta mal:

   1: bajo = enElBalde(a, 0.0, 0.5)

   2: alto = enElBalde(a, 0.5, 1)

Pero con cuatro baldes ya es aparatoso.

   1: balde1 = enElBalde(a, 0.0, 0.25)

   2: balde2 = enElBalde(a, 0.25, 0.5)

   3: balde3 = enElBalde(a, 0.5, 0.75)

   4: balde4 = enElBalde(a, 0.75, 1.0)

Hay dos problemas. Uno es que tenemos que inventar nuevos nombres de variables para cada resultado. El otro es que tenemos que calcular el intervalo de cada balde.

Empezaremos por solucionar el segundo problema. Si el numero de baldes es numBaldes, la anchura de cada balde es 1.0 / numBaldes.

Usaremos un bucle para calcular el intervalo de cada balde. La variable del bucle, i, cuenta de 1 a numBaldes-1:

   1: anchuraBalde = 1.0 / numBaldes

   2: for i in range(numBaldes):

   3: minimo = i * anchuraBalde

   4: maximo = minimo + anchuraBalde

   5: print minimo, "hasta", maximo

Para calcular el límite inferior de cada balde, multiplicamos la variable de bucle por la anchura de balde. El límite superior esta a tan solo una anchuraBalde.

Con numBaldes = 8, la salida es:

   1: 0.0 hasta 0.125

   2: 0.125 hasta 0.25

   3: 0.25 hasta 0.375

   4: 0.375 hasta 0.5

   5: 0.5 hasta 0.625

   6: 0.625 hasta 0.75

   7: 0.75 hasta 0.875

   8: 0.875 hasta 1.0

Puede confirmar que todos los bucles tienen la misma anchura, que no se solapan y que cubren todo el intervalo entre 0,0 y 1,0.

Volvamos ahora al primer problema. Necesitamos un modo de almacenar ocho enteros, usando la variable de bucle para señalarlos uno por uno. En estos momentos debería usted estar pensando “Lista!”.

Debemos crear la lista de baldes fuera del bucle, porque solo queremos hacerlo una vez. Dentro del bucle, podemos llamar repetidamente a enElBalde y actualizar el i-esimo elemento de la lista:

   1: numBaldes = 8

   2: baldes = [0] * numBaldes

   3: anchuraBalde = 1.0 / numBaldes

   4: for i in range(numBaldes):

   5:     minimo = i * anchuraBalde

   6:     maximo = minimo + anchuraBalde

   7:     baldes[i] = enElBalde(lista, minimo, maximo)

   8: print baldes

Con una lista de 1000 valores, este código genera esta lista de baldes:

   1: [138, 124, 128, 118, 130, 117, 114, 131]

Estos números son razonablemente próximos a 125, que es lo que esperábamos.

Por lo menos, están lo bastante cerca como para que podamos pensar que el generador de números aleatorios funciona.

Como ejercicio, compruebe esta función con listas mas largas, y vea
si el numero de valores en cada balde tiende a equilibrarse.

Página 114 de 141

Creado con WordPress & Tema de Anders Norén