![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
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. Sint�cticamente, una tupla es una lista de valores separados por comas:
>>> tupla = 'a', 'b', 'c', 'd', 'e'
Aunque no es necesario, la convenci�n dice que hay que encerrar las tuplas entre par�ntesis:
>>> tupla = ('a', 'b', 'c', 'd', 'e')
Para crear una tupla con un solo elemento, debemos incluir una coma final:
>>> t1 = ('a',)
>>> type(t1)
<type 'tuple'>
Sin la coma, Python trata (\'a') como una cadena entre par�ntesis:
>>> t2 = ('a')
>>> type(t2)
<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.
>>> tupla = ('a', 'b', 'c', 'd', 'e')
>>> tupla[0]
'a'
Y el operador de porci�n selecciona un intervalo de elementos.
>>> tupla[1:3]
('b', 'c')
Pero si intentamos modificar uno de los elementos de la tupla provocaremos un error:
>>> tupla[0] = 'A'
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:
>>> tupla = ('A',) + tupla[1:]
>>> tupla
('A', 'b', 'c', 'd', 'e')
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:
>>> temp = a
>>> a = b
>>> 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:
>>> 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:
>>> a, b, c, d = 1, 2, 3
ValueError: unpack tuple of wrong size
Las funciones pueden devolver tuplas como valor de retorno. Por ejemplo, podr�amos escribir una funci�n que intercambie dos par�metros:
def intercambio(x, y):
return y, x
Luego podemos asignar el valor de retorno a una tupla con dos variables:
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:
def intercambio(x, y): # versi�n incorrecta
x, y = y, x
Si llamamos a esta funci�n as�:
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 qu� no trabaja como usted quiere.
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 c�lculo nos d� siempre el mismo resultado. Para algunas aplicaciones, sin embargo, queremos que el computador sea impredecible. El ejemplo obvio son los juegos, pero hay m�s.
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 m�dulo random contiene una funci�n llamada random que devuelve un n�mero en coma flotante entre 0,0 y 1,0. Cada vez que usted llama a random obtiene el siguiente n�mero de una larga serie. Para ver un ejemplo, ejecute este bucle:
import random
for i in range(10):
x = random.random()
print x
Para generar un n�mero aleatorio entre 0,0 y un l�mite superior como maximo, multiplique x por maximo.
Como ejercicio, genere un n�mero aleatorio entre minimo y maximo.
Como ejercicio adicional, genere un n�mero aleatorio entero entre minimo y maximo, incluyendo ambos extremos.
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 n�mero aleatorio. El valor de retorno es una referencia a la lista completa:
def listaAleatorios(n):
s = [0] * n
for i in range(n):
s[i] = random.random()
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.
>>> listaAleatorios(8)
0.15156642489
0.498048560109
0.810894847068
0.360371157682
0.275119183077
0.328578797631
0.759199803101
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 n�mero de veces que un valor cae en cada balde, deber�amos tener m�s o menos el mismo n�mero en todos.
Podemos contrastar esta teor�a escribiendo un programa que divida el intervalo en baldes y contando el n�mero de valores en cada uno.
Un buen enfoque sobre problemas como �ste 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 n�mero 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 n�mero 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:
cuenta = 0
for car in fruta:
if car == 'a':
cuenta = cuenta + 1
print cuenta
El primer paso es sustituir fruta con lista y car con num. Esto no cambia el programa, s�lo lo hace m�s legible.
El segundo paso es cambiar la comprobaci�n. No estamos interesados en encontrar letras. Queremos ver si num est� entre los valores de minimo y maximo.
cuenta = 0
for num in lista
if minimo < num < maximo:
cuenta = cuenta + 1
print cuenta
El �ltimo paso es encapsular este c�digo en una funci�n llamada enElBalde. Los par�metros son la lista y los valores minimo y maximo.
def enElBalde(lista, minimo, maximo):
cuenta = 0
for num in lista:
if minimo < num < maximo:
cuenta = cuenta + 1
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 solucion�, reutilice la soluci�n.
Tal como aumenta el n�mero de baldes, enElBalde se hace un tanto dif�cil de manejar. Con dos baldes, no est� mal:
bajo = enElBalde(a, 0.0, 0.5)
alto = enElBalde(a, 0.5, 1)
Pero con cuatro baldes ya es aparatoso.
balde1 = enElBalde(a, 0.0, 0.25)
balde2 = enElBalde(a, 0.25, 0.5)
balde3 = enElBalde(a, 0.5, 0.75)
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 n�mero 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:
anchuraBalde = 1.0 / numBaldes
for i in range(numBaldes):
minimo = i * anchuraBalde
maximo = minimo + anchuraBalde
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 est� a tan s�lo una anchuraBalde.
Con numBaldes = 8, la salida es:
0.0 hasta 0.125
0.125 hasta 0.25
0.25 hasta 0.375
0.375 hasta 0.5
0.5 hasta 0.625
0.625 hasta 0.75
0.75 hasta 0.875
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 s�lo queremos hacerlo una vez. Dentro del bucle, podemos llamar repetidamente a enElBalde y actualizar el i-�simo elemento de la lista:
numBaldes = 8
baldes = [0] * numBaldes
anchuraBalde = 1.0 / numBaldes
for i in range(numBaldes):
minimo = i * anchuraBalde
maximo = minimo + anchuraBalde
baldes[i] = enElBalde(lista, minimo, maximo)
print baldes
Con una lista de 1000 valores, este c�digo genera esta lista de baldes:
[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 m�s largas, y vea si el n�mero de valores en cada balde tiende a equilibrarse.
Aunque este programa funciona, no es tan eficiente como podr�a ser. Cada vez que llama a enElBalde recorre la lista entera. Con el aumento del n�mero de baldes, llega a ser un mont�n de recorridos.
Ser�a mejor hacer una sola pasada por la lista y calcular para cada valor el �ndice del balde en el que cae. Luego podemos incrementar el contador apropiado.
En la secci�n anterior tomamos un �ndice, i, y lo multiplicamos por la anchuraBalde para hallar el l�mite inferior de un balde dado. Ahora queremos tomar un valor del intervalo 0,0 a 1,0 y hallar el �ndice del balde en el que cae.
Como el problema es el inverso del anterior, podemos suponer que deber�amos dividir por anchuraBalde en lugar de multiplicar. La suposici�n es correcta.
Como anchuraBalde = 1.0 / numBaldes, dividir por anchuraBalde es lo mismo que multiplicar por numBaldes. Si multiplicamos un n�mero del intervalo que va de 0,0 a 1,0 por numBaldes, obtenemos un n�mero del intervalo entre 0,0 y numBaldes. Si redondeamos ese n�mero al entero inferior obtendremos exactamente lo que estamos buscando, un �ndice de balde:
numBaldes = 8
baldes = [0] * numBaldes
for i in lista:
indice = int(i * numBaldes)
baldes[indice] = baldes[indice] + 1
Usamos la funci�n int para convertir un n�mero en coma flotante en un entero.
�Es posible que este c�lculo genere un �ndice que est� fuera del intervalo (tanto negativo como mayor que len(baldes)-1)?
Una lista como baldes que contiene conteos del n�mero de valores en cada intervalo se llama histograma.
Como ejercicio, escriba una funci�n llamada histograma que tome como par�metros una lista y un n�mero de baldes y devuelva un histograma con el n�mero dado de baldes.
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |