Introprog Py
Introprog Py
Facultad de Ingenier a
Rector: Jorge Humberto Pel aez, S.J. Vicerrector Acad emico: Antonio de Roux Vicerrector del Medio Universitario: Gabriel Jaime P erez, S.J. Facultad de Ingenier a Decano Acad emico: Jorge Francisco Estela Decana del Medio Universitario: Claudia Luc a Mora Titulo: Introduccion a la programacion con Python Titulo original: How to think like a computer scientist, learning with Python Autores: Allen Downey, Jeffrey Elkner, Chris Meyers Traduccion y adaptacion: Andr es Becerra Sandoval Coleccion: Libro ISBN: 978-958-8347-22-6 Coordinador Editorial: Ignacio Murgueitio Email: [email protected] c Derechos Reservados c Sello Editorial Javeriano Correspondencia, suscripciones y solicitudes de canje: Calle 18 # 118-250 Santiago de Cali, Valle del Cauca Ponticia Universidad Javeriana Facultad de Ingenier a Tel efonos: (57-2) 3218200 Exts. 233 - 518 Fax 555 2823 Email: [email protected] Formato 17 x 25 cms Diseno e Impresion: Multimedios PUJ Cali Diseno de Car atula: Patricia Mej a, basada en una imagen de Ken Manheimer http://myriadicity.net Impresion: 2009
Se concede permiso para copiar, distribuir, y/o modicar este documento bajo los terminos de la GNU Free Documentation License, Version 1.1 o cualquier version posterior publicada por la Free Software Foundation; manteniendo sin variaciones las secciones Prologo, Prefacio, y Lista de contribuidores, sin texto de cubierta, y sin texto de contracubierta. Una copia de la licencia est a incluida en el ap endice titulado GNU Free Documentation License y una traduccion de e sta al espanol en el ap endice titulado Licencia de Documentacion Libre de GNU. La GNU Free Documentation License tambi en est a disponible a trav es de www. gnu.org o escribiendo a la Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. A La forma original de este libro es codigo fuente L TEX y compilarlo tiene el efecto de generar un libro de texto en una repesentacion independiente del dispositivo que puede ser convertida a otros formatos e imprimirse. A El codigo fuente L TEX para este libro y mas informacion sobre este proyecto se encuentra en los sitios web: http://cic.puj.edu.co/abecerra http://www.thinkpython.com
A Este libro ha sido preparado utilizando L TEX y las guras se han realizado con xg. Todos estos son programas de codigo abierto, gratuito.
Downey, Allen Introduccion a la programacion con Python / Allen Downey, Jeffrey Elkner, Chris Meyers; traducido y adaptado por Andr es Becerra Sandoval. Santiago de Cali: Ponticia Universidad Javeriana, Sello Editorial Javeriano, 2009. 305 p. ; 26 cm.
ISBN 978-958-8347-22-6
1. Programacion (computadores electronicos) Metodolog a 2. Python (lenguaje de programacion para computadores) I. Meyer, Chris II. Ponticia Universidad Javeriana (Cali) III. How to think like a computer scientist: learning with python IV. T t.
SCDD 005.1
BPUJC
Prologo
Por David Beazley Como un educador, investigador y autor de libro, estoy encantado de ver la terminacion de este texto. Python es un lenguaje de programacion divertido y extremadamente f acil de usar, que ha ganado renombre constantemente en los anos recientes. Desarrollado hace diez anos por Guido van Rossum, la sintaxis simple de Python y su sabor se derivan, en gran parte del ABC, un lenguaje de programacion para ensenanza que se desarrollo en los 1980s. Sin embargo, Python tambi en fue creado para resolver problemas reales y tiene una amplia gama de caracter sticas que se encuentran en lenguajes de programacion como C++, Java, Modula-3, y Scheme. Debido a esto, uno de las caracter sticas notables de Python es la atraccion que ejerce sobre programadores profesionales, cient cos, investigadores, artistas y educadores. A pesar de e sta atraccion en muchas comunidades diversas, usted puede todav a preguntarse porque Python? o porque ensenar programacion con Python? Responder e stas preguntas no es una tarea f acil especialmente cuando la opinion popular est a del lado masoquista de usar alternativas como C++ y Java. Sin embargo, pienso que la respuesta mas directa es que la programacion en Python es simplemente mas divertida y mas productiva. Cuando enseno cursos de inform atica, yo quiero cubrir conceptos importantes, hacer el material interesante y enganchar a los estudiantes. Desafortunadamente, hay una tendencia en la que los cursos de programacion introductorios dedican demasiada atencion en la abstraccion matem atica y a hacer que los estudiantes se frustren con problemas molestos relacionados con la sintaxis, la compilacion y la presencia de reglas arcanas en los lenguajes. Aunque la abstraccion y el formalismo son importantes para los ingenieros de software y para los estudiantes de ciencias de la computacion, usar este enfoque hace a la inform atica muy aburrida. Cuando enseno un curso no quiero tener un grupo de estudiantes sin inspiracion. Quisiera verlos intentando resolver problemas interesantes, explorando ideas diferentes, intentando enfoques no convencionales, rompiendo reglas y aprendiendo de sus errores. En el proceso no quiero perder la mitad del semestre tratando de resolver problemas sint acticos oscuros, interpretando mensajes de error del compilador in-
VI
Prologo
comprensibles, o descifrando cu al de las muchas maneras en que un programa puede generar un error grave de memoria se est a presentando. Una de las razones del por qu e me gusta Python es que proporciona un equilibrio muy bueno entre lo pr actico y lo conceptual. Puesto que se interpreta Python, los principiantes pueden empezar a hacer cosas interesantes casi de inmediato sin perderse en problemas de compilacion y enlace. Adem as, Python viene con una biblioteca grande de modulos, que pueden ser usados en dominios que van desde programacion en la web hasta gr acos. Tener un foco pr actico es una gran manera de enganchar a los estudiantes y permite que emprendan proyectos signicativos. Sin embargo, Python tambi en puede servir como una excelente base para introducir conceptos importantes de la inform atica. Puesto que Python soporta completamente procedimientos y clases, los estudiantes pueden ser introducidos gradualmente a temas como la abstraccion procedimental, las estructuras de datos y la programacion orientada a objetoslo que se puede aplicar despu es a cursos posteriores en Java o C++. Python proporciona, incluso, varias caracter sticas de los lenguajes de programacion funcionales y puede usarse para introducir conceptos que se pueden explorar con m as detalle en cursos con Scheme y Lisp. Leyendo, el prefacio de Jeffrey, estoy sorprendido por sus comentarios de que Python le permita ver un m as alto nivel de e xito y un nivel bajo de frustracion y que puede avanzar mas r apido con mejores resultados. Aunque estos comentarios se reeren a sus cursos introductorios, a veces uso Python por estas mismas razones en los cursos de inform atica avanzada en la Universidad de Chicago. En estos cursos enfrento constantemente la tarea desalentadora de cubrir un monton de material dif cil durante nueve semanas. Aunque es totalmente posible para mi inigir mucho dolor y sufrimiento usando un lenguaje como C++, he encontrado a menudo que este enfoque es improductivoespecialmente cuando el curso se trata de un asunto sin relacion directa con la programacion. He encontrado que usar Python me permite enfocar el tema del curso y dejar a los estudiantes desarrollar proyectos substanciales. Aunque Python siga siendo un lenguaje joven y en desarrollo, creo que tiene un futuro brillante en la educacion. Este libro es un paso importante en esa direccion.
Prefacio
Por Jeff Elkner Este libro debe su existencia a la colaboracion hecha posible por Internet y el movimiento de software libre. Sus tres autoresun profesor de colegio, un profesor de secundaria y un programador profesionaltienen todav a que verse cara a cara, pero han podido trabajar juntos y han sido ayudados por maravillosas personas, quienes han donado su tiempo y energ a para ayudar a hacer ver mejor este libro. Nosotros pensamos que este libro es un testamento a los benecios y futuras posibilidades de esta clase de colaboracion, el marco que se ha puesto en marcha por Richard Stallman y el movimiento de software libre.
VIII
Prefacio
estudiantes tienen en casa. Quer a que fuese un lenguaje de codigo abierto, para que los estudiantes lo pudieran usar en casa sin pagar por una licencia. Quer a un lenguaje usado por programadores profesionales, y que tuviera una comunidad activa alrededor de e l. Ten a que soportar la programacion procedural y orientada a objetos. Y m as importante, ten a que ser f acil de aprender y de ensenar. Cuando investigu e las opciones con estas metas en mente, Python salto como el mejor candidato para la tarea. Ped a uno de los estudiantes m as talentosos de Yorktown, Matt Ahrens, que le diera a Python una oportunidad. En dos meses e l no solo aprendio el lenguaje, sino que escribio una aplicacion llamada pyTicket que permitio a nuestro personal atender peticiones de soporte tecnologico v a web. Sabia que Matt no podr a terminar una aplicacion de esa escala en tan poco tiempo con C++, y esta observacion, combinada con el gravamen positivo de Matt sobre Python, sugirio que este lenguaje era la solucion que buscaba.
IX
texto m as cuidadosamente y de conseguir la correccion del texto por sus lectores cr ticos m as importantes, los estudiantes us andolo para aprender inform atica. Para la segunda parte del libro, enfocada en la programacion orientada a objetos, sab a que alguien con m as experiencia en programacion que yo era necesario para hacer el trabajo correctamente. El libro estuvo incompleto la mayor a del ano hasta que la comunidad de software abierto me proporciono de nuevo los medios necesarios para su terminacion. Recib un correo electronico de Chris Meyers, expresando su inter es en el libro. Chris es un programador profesional que empezo ensenando un curso de programacion el ano anterior, usando Python en el Lane Community College en Eugene, Oregon. La perspectiva de ensenar el curso llevo a Chris al libro, y e l comenzo a ayudarme inmediatamente. Antes del n de ano escolar e l hab a creado un proyecto complementario en nuestro Sitio Web http://www.ibiblio.org/obp, titulado Python for Fun y estaba trabajando con algunos de mis estudiantes m as avanzados como profesor principal, gui andolos mas all a de donde yo pod a llevarlos.
Prefacio
entre dos opciones que no me satisfacen: explicar el #include, void main(), y las sentencias {, y } y arriesgar a confundir o intimidar a algunos de los estudiantes al principio, o decirles, No te preocupes por todo eso ahora; lo retomar e m as tarde, y tomar el mismo riesgo. Los objetivos educativos en este momento del curso son introducir a los estudiantes la idea de sentencia y permitirles escribir su primer programa. Python tiene exactamente lo que necesito para lograr esto, y nada m as. Comparar el texto explicativo de este programa en cada version del libro ilustra m as de lo que esto signica para los estudiantes principiantes. Hay trece p arrafos de explicacion de Hola, mundo! en la version C++; en la version Python, solo hay dos. Aun mas importante, los 11 p arrafos que faltan no hablan de grandes ideas en la programacion de computadores, sino de minucias de la sintaxis de C++. Encontr e la misma situacion al repasar todo el libro. P arrafos enteros desaparec an en la version Python del texto, porque su sencilla sintaxis los hac a innecesarios. Usar un lenguaje de muy alto nivel, como Python, le permite a un profesor posponer los detalles de bajo nivel de la m aquina hasta que los estudiantes tengan el bagaje que necesitan para entenderlos. Permite poner cosas primero pedagogi camente. Unos de los mejores ejemplos de esto es la manera en la que Python maneja las variables. En C++ una variable es un nombre para un lugar que almacena una cosa. Las variables tienen que ser declaradas con tipos, al menos parcialmente, porque el tamano del lugar al cual se reeren tiene que ser predeterminado. As , la idea de una variable se liga con el hardware de la m aquina. El concepto poderoso y fundamental de variable ya es dif cil para los estudiantes principiantes (de inform atica y a lgebra). Bytes y direcciones de memoria no ayudan para nada. En Python una variable es un nombre que se reere a una cosa. Este es un concepto m as intuitivo para los estudiantes principiantes y est a m as cerca del signicado de variable que aprendieron en los cursos de matem atica del colegio. Yo me demor e menos tiempo ayud andolos con el concepto de variable y en su uso este ano, que en el pasado. Otro ejemplo de como Python ayuda en la ensenanza y aprendizaje de la programacion es su sintaxis para las funciones. Mis estudiantes siempre han tenido una gran dicultad comprendiendo las funciones. El problema principal se centra alrededor de la diferencia entre una denicion de funcion y un llamado de funcion, y la distincion relacionada entre un par ametro y un argumento. Python viene al rescate con una bella sintaxis. Una denicion de funcion empieza con la palabra clave def, y simplemente digo a mis estudiantes: cuando denas una funcion, empieza con def, seguido del nombre de la funcion que est as deniendo, cuando llames una funcion, simplemente llama (digita) su nombre. Los par ametros van con las deniciones y los argumentos van con los llamados. No hay tipos de retorno, tipos para los par ametros, o pasos de par ametro por referencia y valor, y ahora yo puedo ensenar funciones en la mitad de tiempo que antes, con una mejor comprension. Usar Python ha mejorado la ecacia de nuestro programa de inform atica para todos los estudiantes. Veo un nivel general de e xito alto y un nivel bajo de la frustra-
XI
cion, que ya hab a experimentado durante los dos anos que ensen e C++. Avanzo m as r apido y con mejores resultados. M as estudiantes terminan el curso con la habilidad de crear programas signicativos; esto genera una actitud positiva hacia la experiencia de la programacion.
Con la publicacion del libro, en forma impresa, espero que continue y se acelere el crecimiento de esta comunidad de usuarios. La emergencia de esta comunidad y la posibilidad que sugiere para otras experiencias de colaboracion similar entre educadores han sido las partes m as excitantes de trabajar en este proyecto, para m . Trabajando juntos, nosotros podemos aumentar la calidad del material disponible para nuestro uso y ahorrar tiempo valioso. Yo les invito a formar parte de nuestra comunidad y espero escuchar de ustedes. Por favor escriba a los autores a [email protected].
XIV
Lista de los colaboradores Courtney Gleason y Katherine Smith escribieron horsebet.py, que se usaba como un caso de estudio en una version anterior de este libro. Su programa se puede encontrar en su website. Lee Harr sometio m as correcciones de las que tenemos espacio para enumerar aqu , y, por supuesto, deber a ser listado como uno de los editores principales del texto. ha enviado numerosas coJames Kaylin es un estudiante usando el texto. El rrecciones. David Kershaw arreglo la funcion erronea imprimaDoble en la Seccion 3.10. coEddie Lam ha enviado numerosas correcciones a los Cap tulos 1, 2, y 3. El rrigio el Makele para que creara un ndice, la primera vez que se compilaba el documento, y nos ayudo a instalar un sistema de control de versiones. Man-Yong Lee envio una correccion al codigo de ejemplo en la Seccion 2.4. David Mayo noto que la palabra inconscientementedebe cambiarse por subconscientemente. Chris McAloon envio varias correcciones a las Secciones 3.9 y 3.10. Matthew J. Moelter ha sido un contribuidor de mucho tiempo quien remitio numerosas correcciones y sugerencias al libro. Simon Dicon Montford reporto una denicion de funcion que faltaba y varios tambi errores en el Cap tulo 3. El en encontro errores en la funcion incrementar del Cap tulo 13. John Ouzts corrigio la denicion de valor de retorno en el Cap tulo 3. Kevin Parks envio sugerencias valiosas para mejorar la distribucion del libro. David Pool envio la correccion de un error en el glosario del Cap tulo 1 y palabras de est mulo. Michael Schmitt envio una correccion al cap tulo de archivos y excepciones. Robin Shaw noto un error en la Seccion 13.1, donde la funcion imprimirHora se usaba en un ejemplo sin estar denida. Paul Sleigh encontro un error en el Cap tulo 7 y otro en los guiones de Perl de Jonah Cohen que generan HTML a partir de LaTeX.
XV
Craig T. Snydal est a probando el texto en un curso en Drew University. El ha aportado varias sugerencias valiosas y correcciones. Ian Thomas y sus estudiantes est an usando el texto en un curso de programacion. Ellos son los primeros en probar los cap tulos de la segunda mitad del libro y han enviado numerosas correcciones y sugerencias. Keith Verheyden envio una correccion al Cap tulo 3. Peter Winstanley descubrio un viejo error en nuestro Lat n, en el cap tulo 3. Chris Wrobel hizo correcciones al codigo en el cap tulo sobre archivos, E/S y excepciones. Moshe Zadka hizo contribuciones inestimables a este proyecto. Adem as de escribir el primer bosquejo del cap tulo sobre Diccionarios, tambi en proporciono una direccion continua en los primeros anos del libro. Christoph Zwerschke envio varias correcciones y sugerencias pedagogicas, y explico la diferencia entre gleich y selbe. James Mayer nos envio una ci enaga entera de errores tipogr acos y de deletreo, incluyendo dos en la lista de colaboradores Hayden McAfee descubrio una inconsistencia potencialmente confusa entre dos ejemplos. Angel Arnal hace parte de un equipo internacional de traductores que traba tambi jan en la version espanola del texto. El en ha encontrado varios errores en la version inglesa.
Traduccion al espanol
Al comienzo de junio de 2007 tom e la iniciativa de traducir el texto How to think like a Computer Scientist, with Python al espanol. R apidamente me d cuenta de que ya hab a un trabajo inicial de traduccion empezado por: Angel Arnal I Juanes Litza Amurrio Efrain Andia Ellos hab an traducido los cap tulos 1,2,10,11, y 12, as como el prefacio, la introduccion y la lista de colaboradores. Tom e su valioso trabajo como punto de partida, adapt e los cap tulos, traduje las secciones faltantes del libro y anad un primer cap tulo adicional sobre solucion de problemas. Para realizar este trabajo ha sido invaluable la colaboracion de familiares, colegas, amigos y estudiantes que han senalado errores, expresiones confusas y han aportado toda clase de sugerencias constructivas. Mi agradecimiento va para los traductores antes mencionados y para: Beatriz Eugenia Mar n, mi esposa, quien encontro varios errores en el texto. Los profesores que dictan el curso Introduccion a la Programacion, en la Ponticia Universidad Javeriana (Cali-Colombia): Mario Juli an Mora
Indice general
Prologo Prefacio Lista de los colaboradores Traduccion al espanol 1. Solucion de problemas 1.1. Solucion de acertijos . . . . . . . . . . . . . . . . . . . 1.2. El m etodo de solucion . . . . . . . . . . . . . . . . . . 1.3. Reexion sobre este m etodo de solucion . . . . . . . . 1.4. Acertijos propuestos . . . . . . . . . . . . . . . . . . . 1.5. Mas all a de los acertijos: problemas computacionales 1.6. Problemas computacionales propuestos . . . . . . . . 1.7. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . 2. El camino hacia el programa 2.1. El lenguaje de programacion Python . . . 2.2. Qu e es un programa? . . . . . . . . . . . 2.3. Qu e es la depuracion (debugging)? . . . 2.4. Lenguajes formales y lenguajes naturales 2.5. El primer programa . . . . . . . . . . . . . 2.6. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
V VII XIII XVII
1 . 1 . 4 . 6 . 7 . 7 . 9 . 10 . . . . . . . . . . . 11 11 13 14 16 18 18 21 21 22 23 24 25
3. Variables, expresiones y sentencias 3.1. Valores y tipos . . . . . . . . . . . . . . . . . . 3.2. Variables . . . . . . . . . . . . . . . . . . . . . 3.3. Nombres de variables y palabras reservadas 3.4. Sentencias . . . . . . . . . . . . . . . . . . . . 3.5. Evaluando expresiones . . . . . . . . . . . . .
XX
Indice general 3.6. 3.7. 3.8. 3.9. 3.10. 3.11. Operadores y operandos . . Orden de las operaciones . . Operaciones sobre cadenas . Composicion . . . . . . . . . Comentarios . . . . . . . . . Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 26 27 28 29 29 31 31 32 32 33 34 35 37 37 38 39 40 41 42 45 45 45 46 47 47 48 49 50 50 52 53 54 55 57 57 59 61 62
4. Funciones 4.1. Llamadas a funciones . . . . . . . . . . . . 4.2. Conversion de tipos . . . . . . . . . . . . . 4.3. Coercion de tipos . . . . . . . . . . . . . . 4.4. Funciones matem aticas . . . . . . . . . . . 4.5. Composicion . . . . . . . . . . . . . . . . . 4.6. Agregando nuevas funciones . . . . . . . 4.7. Deniciones y uso . . . . . . . . . . . . . . 4.8. Flujo de ejecucion . . . . . . . . . . . . . . 4.9. Par ametros y argumentos . . . . . . . . . 4.10. Las variables y los par ametros son locales 4.11. Diagramas de pila . . . . . . . . . . . . . . 4.12. Funciones con resultados . . . . . . . . . . 4.13. Glosario . . . . . . . . . . . . . . . . . . .
5. Condicionales y recursion 5.1. El operador residuo . . . . . . . . . . . . . . . 5.2. Expresiones booleanas . . . . . . . . . . . . . 5.3. Operadores logicos . . . . . . . . . . . . . . . 5.4. Ejecucion condicional . . . . . . . . . . . . . . 5.5. Ejecucion alternativa . . . . . . . . . . . . . . 5.6. Condicionales encadenados . . . . . . . . . . 5.7. Condicionales anidados . . . . . . . . . . . . 5.8. La sentencia return . . . . . . . . . . . . . . 5.9. Recursion . . . . . . . . . . . . . . . . . . . . . 5.10. Diagramas de pila para funciones recursivas 5.11. Recursion innita . . . . . . . . . . . . . . . . 5.12. Entrada por el teclado . . . . . . . . . . . . . 5.13. Glosario . . . . . . . . . . . . . . . . . . . . . 6. Funciones fruct feras 6.1. Valores de fetorno . . . . 6.2. Desarrollo de programas 6.3. Composicion . . . . . . . 6.4. Funciones booleanas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Indice general 6.5. 6.6. 6.7. 6.8. 6.9. M as recursion . . El salto de fe . . . Un ejemplo m as . Chequeo de tipos Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
XXI
63 65 66 67 68 69 69 70 72 74 75 76 76 77 79 80 81 81 82 82 84 84 85 86 86 87 88 89 91 91 92 93 94 95 95 96 96 97 98
7. Iteracion 7.1. Asignacion multiple . . . . . . . . 7.2. La sentencia while (mientras) 7.3. Tablas . . . . . . . . . . . . . . . . . 7.4. Tablas de dos dimensiones . . . . . 7.5. Encapsulamiento y generalizacion 7.6. M as encapsulamiento . . . . . . . . 7.7. Variables locales . . . . . . . . . . . 7.8. Mas generalizacion . . . . . . . . . 7.9. Funciones . . . . . . . . . . . . . . 7.10. Glosario . . . . . . . . . . . . . . .
8. Cadenas 8.1. Un tipo de dato compuesto . . . . . 8.2. Longitud . . . . . . . . . . . . . . . . 8.3. Recorridos en cadenas y el ciclo for 8.4. Segmentos de cadenas . . . . . . . . 8.5. Comparacion de cadenas . . . . . . . 8.6. Las cadenas son inmutables . . . . . 8.7. Una funcion buscar . . . . . . . . . 8.8. Iterando y contando . . . . . . . . . . 8.9. El modulo string . . . . . . . . . . 8.10. Clasicacion de car acteres . . . . . . 8.11. Glosario . . . . . . . . . . . . . . . . 9. Listas 9.1. 9.2. 9.3. 9.4. 9.5. 9.6. 9.7. 9.8. 9.9. 9.10. Creacion de listas . . . . . . . Accediendo a los elementos . Longitud de una lista . . . . . Pertenencia . . . . . . . . . . . Listas y ciclos for . . . . . . . Operaciones sobre listas . . . Segmentos de listas . . . . . . Las listas son mutables . . . . Otras operaciones sobre listas Objetos y valores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
XXII
Indice general Alias . . . . . . . . . . . Clonando listas . . . . . Listas como par ametros Listas anidadas . . . . . Matrices . . . . . . . . . Cadenas y listas . . . . . Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 99 100 101 102 102 103 105 105 106 107 108 108 109 110 112 113 115 116 117 118 118 120 121 122 123 125 127 128 131 131 132 134 137 137 138 139 139
10. Tuplas 10.1. Mutabilidad y tuplas . . . . . . . 10.2. Asignacion de tuplas . . . . . . . 10.3. Tuplas como valores de retorno . 10.4. Numeros aleatorios . . . . . . . . 10.5. Lista de numeros aleatorios . . . 10.6. Conteo . . . . . . . . . . . . . . . 10.7. Muchas regiones . . . . . . . . . . 10.8. Una solucion en una sola pasada 10.9. Glosario . . . . . . . . . . . . . . 11. Diccionarios 11.1. Operaciones sobre diccionarios 11.2. M etodos del diccionario . . . . 11.3. Copiado y alias . . . . . . . . . 11.4. Matrices dispersas . . . . . . . . 11.5. Pistas . . . . . . . . . . . . . . . 11.6. Enteros largos . . . . . . . . . . 11.7. Contar letras . . . . . . . . . . . 11.8. Glosario . . . . . . . . . . . . . 12. Archivos y excepciones 12.1. Archivos de texto 12.2. Escribir variables 12.3. Directorios . . . . 12.4. Encurtido . . . . . 12.5. Excepciones . . . 12.6. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
13. Clases y objetos 13.1. Tipos compuestos denidos por el usuario 13.2. Atributos . . . . . . . . . . . . . . . . . . . . 13.3. Instancias como par ametro . . . . . . . . . . 13.4. Mismidad . . . . . . . . . . . . . . . . . . .
Indice general 13.5. 13.6. 13.7. 13.8. 13.9. Rect angulos . . . . . . . . . . . . . Instancias como valores de retorno Los objetos son mutables . . . . . . Copiado . . . . . . . . . . . . . . . Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
XXIII
. . . . .
141 142 142 143 145 147 147 148 149 150 151 152 152 153 155 155 156 157 158 159 160 161 162 164 165 167 167 167 168 170 171 171 173 174 175
14. Clases y funciones 14.1. Hora . . . . . . . . . . . . . . . . . . . . . . 14.2. Funciones puras . . . . . . . . . . . . . . . . 14.3. Modicadoras . . . . . . . . . . . . . . . . . 14.4. Cual es el mejor estilo? . . . . . . . . . . . 14.5. Desarrollo con prototipos vs. planicacion . 14.6. Generalizacion . . . . . . . . . . . . . . . . . 14.7. Algoritmos . . . . . . . . . . . . . . . . . . . 14.8. Glosario . . . . . . . . . . . . . . . . . . . . 15. Clases y m etodos 15.1. Caracter sticas de orientacion a objetos 15.2. imprimirHora . . . . . . . . . . . . . 15.3. Otro ejemplo . . . . . . . . . . . . . . . 15.4. Un ejemplo m as complejo . . . . . . . 15.5. Argumentos opcionales . . . . . . . . 15.6. El m etodo de inicializacion . . . . . . . 15.7. Reconsiderando la clase Punto . . . . . 15.8. Sobrecarga de operadores . . . . . . . 15.9. Polimorsmo . . . . . . . . . . . . . . 15.10. Glosario . . . . . . . . . . . . . . . . . 16. Conjuntos de objetos 16.1. Composicion . . . . . . . . . . . . . . 16.2. Objeto Carta . . . . . . . . . . . . . 16.3. Atributos de clase y el m etodo str 16.4. Comparando cartas . . . . . . . . . . 16.5. Mazos . . . . . . . . . . . . . . . . . . 16.6. Imprimiendo el mazo . . . . . . . . . 16.7. Barajando el mazo . . . . . . . . . . . 16.8. Eliminando y entregando cartas . . . 16.9. Glosario . . . . . . . . . . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
XXIV
Indice general 177 177 178 179 180 181 181 183 187 189 189 189 191 192 193 193 194 195 196 197 197 199 199 200 200 201 202 202 203 204 204 207 207 208 209 209 211 212 214
17. Herencia 17.1. Denicion . . . . . . . . . . 17.2. Una mano de cartas . . . . . 17.3. Repartiendo cartas . . . . . 17.4. Imprimiendo una mano . . 17.5. La clase JuegoDeCartas . 17.6. La clase ManoSolterona . 17.7. La clase JuegoSolterona 17.8. Glosario . . . . . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
18. Listas enlazadas 18.1. Referencias incrustadas . . . . . . . . . . . . 18.2. La clase Nodo . . . . . . . . . . . . . . . . . 18.3. Listas como colecciones . . . . . . . . . . . . 18.4. Listas y recursion . . . . . . . . . . . . . . . 18.5. Listas innitas . . . . . . . . . . . . . . . . . 18.6. El teorema de la ambiguedad fundamental 18.7. Modicando listas . . . . . . . . . . . . . . . 18.8. Funciones facilitadoras y auxiliares . . . . . 18.9. La clase ListaEnlazada . . . . . . . . . . 18.10. Invariantes . . . . . . . . . . . . . . . . . . . 18.11. Glosario . . . . . . . . . . . . . . . . . . . . 19. Pilas 19.1. 19.2. 19.3. 19.4. 19.5. 19.6. 19.7. 19.8. 19.9. 20. Colas 20.1. 20.2. 20.3. 20.4. 20.5. 20.6. 20.7.
Tipos abstractos de datos . . . . . . . . . . . . . . . . El TAD Pila . . . . . . . . . . . . . . . . . . . . . . . . Implementando pilas por medio de listas de Python Meter y sacar . . . . . . . . . . . . . . . . . . . . . . . Evaluando expresiones postjas con una Pila . . . . An alisis sint actico . . . . . . . . . . . . . . . . . . . . Evaluando expresiones postjas . . . . . . . . . . . . Clientes y proveedores . . . . . . . . . . . . . . . . . Glosario . . . . . . . . . . . . . . . . . . . . . . . . . El TAD Cola . . . . . . . Cola enlazada . . . . . . Desempeno . . . . . . . . Cola Enlazada mejorada Cola de prioridad . . . . La clase golfista . . . Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Indice general 21. Arboles 21.1. Construyendo a rboles . . . . . . . . . . . . 21.2. Recorridos sobre a rboles . . . . . . . . . . . 21.3. Arboles de expresiones . . . . . . . . . . . . 21.4. Recorrido en a rboles . . . . . . . . . . . . . 21.5. Construyendo un a rbol para una expresion 21.6. Manejo de errores . . . . . . . . . . . . . . . 21.7. El a rbol de animales . . . . . . . . . . . . . . 21.8. Glosario . . . . . . . . . . . . . . . . . . . .
XXV
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
215 216 217 217 218 220 225 225 228 231 231 233 237 241 242 243 244 245 246 246 247 247 248 249 253 254 255 258 259 260 261
A. Depuracion A.1. Errores sint acticos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.2. Errores en tiempo de ejecucion . . . . . . . . . . . . . . . . . . . . . A.3. Errores sem anticos . . . . . . . . . . . . . . . . . . . . . . . . . . . . B. Creando un nuevo tipo de datos B.1. Multiplicacion de fracciones B.2. Suma de fracciones . . . . . B.3. El algoritmo de Euclides . . B.4. Comparando fracciones . . B.5. Extendiendo las fracciones . B.6. Glosario . . . . . . . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
C. Programas completos C.1. Clase punto . . . . . . . . . . . . . C.2. Clase hora . . . . . . . . . . . . . . C.3. Cartas, mazos y juegos . . . . . . . C.4. Listas enlazadas . . . . . . . . . . . C.5. Clase pila . . . . . . . . . . . . . . . C.6. Colas PEPS y de colas de prioridad C.7. Arboles . . . . . . . . . . . . . . . . C.8. Arboles de expresiones . . . . . . . C.9. Adivinar el animal . . . . . . . . . C.10. Clase Fraccion . . . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
D. Lecturas adicionales recomendadas 263 D.1. Libros y sitios web relacionados con Python . . . . . . . . . . . . . . 264 D.2. Libros generales de ciencias de la computacion recomendados . . . 265
XXVI
Indice general 267 268 269 269 270 272 272 273 273 273 273 274 275 276 278 278 279 281 281 282 282 282 283 283
E. GNU Free Documentation License E.1. Applicability and Denitions . . . . . . . . . . . . . . . . . E.2. Verbatim Copying . . . . . . . . . . . . . . . . . . . . . . . . E.3. Copying in Quantity . . . . . . . . . . . . . . . . . . . . . . E.4. Modications . . . . . . . . . . . . . . . . . . . . . . . . . . E.5. Combining Documents . . . . . . . . . . . . . . . . . . . . . E.6. Collections of Documents . . . . . . . . . . . . . . . . . . . E.7. Aggregation with Independent Works . . . . . . . . . . . . E.8. Translation . . . . . . . . . . . . . . . . . . . . . . . . . . . . E.9. Termination . . . . . . . . . . . . . . . . . . . . . . . . . . . E.10. Future Revisions of This License . . . . . . . . . . . . . . . E.11. Addendum: How to Use This License for Your Documents F. Licencia de documentacion libre de GNU F.1. Aplicabilidad y deniciones . . . . . . . . . . . . . . . . F.2. Copia literal . . . . . . . . . . . . . . . . . . . . . . . . . F.3. Copiado en cantidad . . . . . . . . . . . . . . . . . . . . F.4. Modicaciones . . . . . . . . . . . . . . . . . . . . . . . . F.5. Combinacion de documentos . . . . . . . . . . . . . . . F.6. Colecciones de documentos . . . . . . . . . . . . . . . . F.7. Agregacion con trabajos independientes . . . . . . . . . F.8. Traduccion . . . . . . . . . . . . . . . . . . . . . . . . . . F.9. Terminacion . . . . . . . . . . . . . . . . . . . . . . . . . F.10. Revisiones futuras de esta licencia . . . . . . . . . . . . F.11. ADENDA: Como usar esta Licencia en sus documentos . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
Cap tulo 1
Solucion de problemas
1.1. Solucion de acertijos
Todos nos hemos topado con acertijos como el siguiente. Disponga los d gitos del 1 al 9 en el recuadro siguiente, de manera que la suma de cada la, cada columna y las dos diagonales d e el mismo resultado:
Este acertijo se denomina construccion de un cuadrado m agico. Un acertijo, normalmente, es un enigma o adivinanza que se propone como pasatiempo. Otros ejemplos de acertijo son un crucigrama, una sopa de letras y un sudoku. Los acertijos pueden tener varias soluciones, por ejemplo, la siguiente es una solucion propuesta al acertijo anterior: 1 4 7 2 5 8 3 6 9
Usted puede notar que esta solucion candidata no es correcta. Si tomamos la suma por las, obtenemos valores distintos: En la la 1: 1+2+3=6 En la la 2: 4+5+6=15 En la la 3: 7+8+9=24
Solucion de problemas
Si tomamos las columnas, tampoco obtenemos el mismo resultado en cada suma: En la columna 1: 1+4+7=12 En la columna 2: 2+5+8=15 En la columna 3: 3+6+9=18 A pesar de que las diagonales s suman lo mismo: En la diagonal 1: 1+5+9=15 En la diagonal 2: 7+5+3=15 Tomese un par de minutos para resolver este acertijo, es decir, para construir un cuadrado m agico y regrese a la lectura cuando obtenga la solucion. Ahora, responda para s mismo las siguientes preguntas: Cu al es la solucion que encontro? Es correcta su solucion? Como le demuestra a alguien que su solucion es correcta? Cu al fue el proceso de solucion que llevo a cabo en su mente? Como le explicar a a alguien el proceso de solucion que llevo a cabo? Puede poner por escrito el proceso de solucion que llevo a cabo? El reexionar seriamente sobre estas preguntas es muy importante, tenga la seguridad de que esta actividad ser a muy importante para continuar con la lectura. Vamos a ir contestando las preguntas desde una solucion particular, y desde un proceso de solucion particular, el de los autores. Su solucion y su proceso de solucion son igualmente valiosos, el nuestro solo es otra alternativa; es m as, puede que hayamos descubierto la misma: 4 3 8 9 5 1 2 7 6
Esta solucion es correcta, porque la suma por las, columnas y de las dos diagonales da el mismo valor, 15. Ahora, para demostrarle a alguien este hecho podemos revisar las sumas por las, columnas y diagonales detalladamente. El proceso de solucion que llevamos a cabo fue el siguiente:
Sospech abamos que el 5 deb a estar en la casilla central, ya que es el numero medio de los 9: 1 2 3 4 5 6 7 8 9. Observamos un patron interesante de la primera solucion propuesta: las diagonales sumaban igual, 15: 1 4 7 2 5 8 3 6 9
La observacion anterior, 1+5+9=7+5+3, tambi en permite deducir otro hecho interesante. Como el 5 est a en las dos sumas, podemos deducir que 1+9=7+3, y esto es 10. Una posible estrategia consiste en colocar parejas de numeros que sumen 10, dejando al 5 emparedado, por ejemplo, poner la pareja 6,4: 6 5 4 Para agilizar e sta estrategia es conveniente enumerar todas las parejas de numeros entre 1 y 9 que suman 10, excluyendo al 5: (1,9),(2,8),(3,7),(4,6) Ahora, podemos probar colocando e stas parejas en las, columnas y diagonales. Un primer ensayo: 7 8 6 5 4 2 3
Aqu encontramos que es imposible armar el cuadrado, pues no hay como situar el 9 ni el 1. Esto sugiere que la pareja (6,4) no va en la columna central, debemos cambiarla. Despu es de varios ensayos, moviendo la pareja (6,4), y reacomodando los otros numeros logramos llegar a la solucion correcta.
Solucion de problemas
Para deducir m as hechos, a partir de los datos recolectados y los detalles inusuales, hay que usar todas las armas del razonamiento: el poder de la deduccion (derivar nuevos hechos a partir de los datos), la induccion (generalizar a partir de casos), la refutacion (el proceso de probar la falsedad de alguna conjetura), el pensamiento analogico (encontrando relaciones,met aforas, analog as) y, por ultimo, pero no menos importante, el uso del sentido comun. Despu es del an alisis de datos, uno siempre debe proponer una alternativa de solucion, as sea simple e ingenua, y proceder intentando probarla y refutarla al mismo tiempo. Vale la pena recalcar esto: no importa que tan ingenua, sencilla e incompleta es una alternativa de solucion, con tal de que nos permita seguir indagando. Esto es como la primera frase que se le dice a una chica (o chico, dado el caso) que uno quiere conocer; no importa qu e frase sea, no importa que tan trivial sea, con tal de que permita iniciar una conversacion. El proceso de busqueda de soluciones es como una conversacion que se inicia, aunque en este caso el interlocutor no es una persona, sino el problema que tenemos a mano. Con una primera alternativa de solucionno importa lo sencilla e incompleta podemos formularnos una pregunta interesante: resuelve esta alternativa el problema? Lo importante de responder la pregunta anterior no es la obtencion de un No como respuesta; pues esto es lo que sucede la mayor a de las veces. Lo importante viene cuando nos formulamos esta segunda pregunta Con lo que sabemos del problema hasta ahora por qu e mi alternativa no es capaz de resolverlo? La respuesta a la pregunta anterior puede ser: todav a no s e lo suciente sobre el problema para entender por qu e mi alternativa de solucion no lo resuelve; esto es una senal de alerta para recolectar m as datos y estudiar mas el dominio del problema. Una respuesta m as constructiva a la pregunta anterior puede ser: mi alternativa de solucion no resuelve el problema porque no considera algunos hechos importantes, y no considera algunas restricciones que debe cumplir una solucion. Un ejemplo de este tipo de respuesta lo da nuestro ensayo de colocar la pareja (6,4) emparedando al 5 en el problema del cuadrado m agico: 7 8 6 5 4 2 3
Solucion de problemas Cuando notamos que la pareja (6,4) no puede colocarse en la columna central del cuadrado, intentamos otra alternativa de solucion, colocando estos numeros en las o en las esquinas. Lo importante de este tipo de respuesta es que nos va a permitir avanzar a otra alternativa de solucion, casi siempre m as compleja y m as cercana a la solucion. No poner obst aculos a la creatividad. Es muy dif cil lograr esto porque la mente humana siempre busca l mites para respetar; as , hay que realizar un esfuerzo consciente para eliminar todo l mite o restriccion que nuestra mente va creando. Una estrategia interesante es el uso y fortalecimiento del pensamiento lateral. Perseverar. El motivo m as comun de fracaso en la solucion de acertijos y problemas es el abandono. No existe una receta m agica para resolver problemas, lo unico que uno puede hacer es seguir un m etodo y perseverar, perseverar sin importar cuantas alternativas de solucion incorrectas se hayan generado. Esta es la clave para el e xito. Holmes dec a que eran much simos m as los casos que no hab a podido resolver, quiz as Grissom reconocer a lo mismo. Lo importante entonces es perseverar ante cada nuevo caso, esta es la unica actitud razonable para enfrentar y resolver problemas.
a a a b2
2 2
= = =
b ba ba b2 b(a b) b 2b 2 2
(a b)(a + b) = a+b = a a b 1 = = =
Tenga en cuenta que en el ultimo paso se volvio a usar el hecho de que a = b, = 1 . en la forma a b 3. Encuentre el menor numero entero positivo que pueda descomponerse como la suma de los cubos de dos numeros enteros positivos de dos maneras distintas. Esto es, encontrar el m nimo A tal que A = b3 + c3 y A = d3 + e3 , con A, b, c, d y e numeros positivos, mayores a cero, y distintos entre si.
Solucion de problemas
blema computacional tiene como solucion la descripcion de un conjunto de pasos que se podr an llevar a cabo de manera general para lograr un objetivo. Un ejemplo que ilustra esto es la multiplicacion. Todos sabemos multiplicar nume ros de dos cifras, por ejemplo: 3 2 3 8 1 4 1 4 4
+ 6 7
Pero el problema computacional asociado a la multiplicacion de numeros de dos cifras consiste en hallar la descripcion general de todos los procesos posibles de multiplicacion de parejas de numeros de dos cifras. Este problema ha sido resuelto, desde hace varios milenios, por diferentes civilizaciones humanas, siguiendo m etodos alternativos. Un m etodo de solucion moderno podr a describirse as : Tome los dos numeros de dos cifras, P y Q. Suponga que las cifras de P son p1 y p2 , esto es, P = p1 p2 . Igualmente, suponga que Q = q1 q2 . La descripcion general de todas las multiplicaciones de dos cifras puede hacerse as : p1 q1 q2 p1 q1 p2 q2 p1 + q1 p2 p2 q2 q2 p2 q2 p2
+ q1 p1 q1 p1
Tome la cifra q2 y multipl quela por las cifras de P (con ayuda de una tabla de multiplicacion). Ubique los resultados debajo de cada cifra de P correspondiente. Tome la cifra q1 y multipl quela por las cifras de P (con ayuda de una tabla de multiplicacion). Ubique los resultados debajo de las cifras que se generaron en el paso anterior, aunque desplazadas una columna hacia la izquierda. Si en alguno de los pasos anteriores el resultado llega a 10 o se pasa de 10, ubique las unidades unicamente y lleve un acarreo, en decenas o centenas) para la columna de la izquierda. Sume los dos resultados parciales, obteniendo el resultado nal. Usted puede estar quej andose en este momento, para qu e hay que complicar tanto nuestro viejo y conocido proceso de multiplicacion? Bueno, hay varias razones para esto:
Una descripcion impersonal como e sta puede ser le da y ejecutada por cualquier persona o computador, como veremos mas adelante. Solo creando descripciones generales de procesos se pueden analizar para demostrar que funcionan correctamente. Quer amos sorprenderlo, tomando algo tan conocido como la suma y d andole una presentacion que, quiz as, nunca hab a visto. Este cambio de perspectiva es una invitacion a que abra su mente a pensar en descripciones generales de procesos. Precisando un poco, un problema computacional es la descripcion general de una situacion en la que se presentan unos datos de entrada y una salida deseada que se quiere calcular. Por ejemplo, en el problema computacional de la multiplicacion de numeros de dos cifras, los datos de entrada son los numeros a multiplicar; la salida es el producto de los dos numeros. Existen m as problemas computacionales como el de ordenar un conjunto de numeros y el problema de encontrar una palabra en un p arrafo de texto. Como ejercicio dena para estos problemas cuales son los datos de entrada y la salida deseada. La solucion de un problema computacional es una descripcion general del conjunto de pasos que se deben llevar a cabo con las entradas del problema para producir los datos de salida deseados. Solucionar problemas computacionales no es muy diferente de solucionar acertijos, las dos actividades producen la misma clase de retos intelectuales, y el m etodo de solucion de la seccion 1.2 es aplicable en los dos casos. Lo unico que hay que tener en cuenta es que la solucion de un problema es una descripcion general o programa, como veremos m as adelante, que se reere a las entradas y salidas de una manera m as t ecnica de lo que estamos acostumbrados. Un ejemplo de esto lo constituyen los nombres p1 , p2 , q1 y q2 que usamos en la descripcion general de la multiplicacion de numeros de dos cifras. Aunque la solucion de problemas es una actividad compleja, es muy interesante, estimulante e intelectualmente graticante; incluso cuando no llegamos a solucionar los problemas completamente. En el libro vamos a enfocarnos en la solucion de problemas computacionales por medio de programas, y, aunque solo vamos a explorar este tipo de problemas, usted ver a que las estrategias de solucion, los conceptos que aprender a y la actitud de cient co de la computacion que adquirir a ser an valiosas herramientas para resolver todo tipo de problemas de la vida real.
Solucion de problemas
Describa como encontrar el menor elemento en un conjunto de numeros. Describa como encontrar una palabra dentro de un texto m as largo.
1.7. Glosario
Acertijo: enigma o adivinanza que se propone como pasatiempo. Solucion de problemas: el proceso de formular un problema, hallar la solucion y expresar la solucion. M etodo de solucion de problemas: un conjunto de pasos, estrategias y t ecnicas organizados que permiten solucionar problemas de una manera ordenada. Problema: una situacion o circunstancia en la que se diculta lograr un n. Problema computacional: una situacion general con una especicacion de los datos de entrada y los datos de salida deseados. Solucion a un problema: conjunto de pasos y estrategias que permiten lograr un n determinado en una situacion problem atica, cumpliendo ciertas restricciones. Solucion a un problema computacional: descripcion general de los pasos que toman cualquier entrada en un problema computacional y la transforman en una salida deseada. Restriccion: una condicion que tiene que cumplirse en un problema dado.
Cap tulo 2
12
o lenguajes ensambladores. A proposito, las computadoras solo ejecutan programas escritos en lenguajes de bajo nivel. Los programas de alto nivel tienen que ser traducidos antes de ser ejecutados. Esta traduccion lleva tiempo, lo cual es una pequena desventaja de los lenguajes de alto nivel. Aun as , las ventajas son enormes. En primer lugar, la programacion en lenguajes de alto nivel es mucho m as f acil; escribir programas en un lenguaje de alto nivel toma menos tiempo ya que los programas son m as cortos, m as f aciles de leer, y es m as probable que queden correctos. En segundo lugar, los lenguajes de alto nivel son portables, lo que signica que los programas escritos con estos pueden ser ejecutados en tipos diferentes de computadoras sin modicacion alguna o con pocas modicaciones. Programas escritos en lenguajes de bajo nivel solo pueden ser ejecutados en un tipo de computadora y deben ser reescritos para ser ejecutados en otra. Debido a estas ventajas, casi todo programa se escribe en un lenguaje de alto nivel. Los lenguajes de bajo nivel son solo usados para unas pocas aplicaciones especiales. Hay dos tipos de programas que traducen lenguajes de alto nivel a lenguajes de bajo nivel: int erpretes y compiladores. Una int erprete lee un programa de alto nivel y lo ejecuta, lo que signica que lleva a cabo lo que indica el programa. Traduce el programa poco a poco, leyendo y ejecutando cada comando.
Cdigo Fuente
Intrprete
Salida
Un compilador lee el programa y lo traduce todo al mismo tiempo, antes de ejecutar alguno de los programas. A menudo se compila un programa como un paso aparte, y luego se ejecuta el codigo compilado. En este caso, al programa de alto nivel se lo llama el codigo fuente, y al programa traducido es llamado el codigo de objeto o el codigo ejecutable.
Cdigo Fuente Cdigo Objeto
Compilador
Ejecutor
Salida
A Python se lo considera un lenguaje interpretado, porque sus programas son ejecutados por un int erprete. Existen dos maneras de usar el int erprete: modo de comando y modo de guion. En modo de comando se escriben sentencias en el lenguaje Python y el int erprete muestra el resultado. $ python Python 2.6.2 (r262:71600, Apr 30 2009, 20:29:58)
2.2 Qu e es un programa? [GCC Type more >>>1 2 4.3.3] on linux2 "help", "copyright", "credits" or "license" for information. + 1
13
La primera l nea de este ejemplo es el comando que pone en marcha al int erprete de Python. Las dos l neas siguientes son mensajes del int erprete. La tercera l nea comienza con >>>, lo que indica que el int erprete est a listo para recibir comandos. Escribimos 1+1 y el int erprete contesto 2. Alternativamente, se puede escribir el programa en un archivo y usar el int erprete para ejecutar el contenido de dicho archivo. El archivo, en este caso, se denomina un guion (script); por ejemplo, en un editor de texto se puede crear un archivo unomasuno.py que contenga esta l nea: print 1 + 1 Por acuerdo un anime, los archivos que contienen programas de Python tienen nombres que terminan con .py. Para ejecutar el programa, se le tiene que indicar el nombre del guion a la interpretadora. $ python unomasuno.py 2 En otros entornos de desarrollo, los detalles de la ejecucion de programas diferir an. Adem as, la mayor a de programas son m as interesantes que el anterior. La mayor a de ejemplos en este libro son ejecutados en la l nea de comandos. La l nea de comandos es m as conveniente para el desarrollo de programas y para pruebas r apidas, porque las instrucciones de Python se pueden pasar a la m aquina para ser ejecutadas inmediatamente. Una vez que el programa est a completo, se lo puede archivar en un guion para ejecutarlo o modicarlo en el futuro.
2.2. Qu e es un programa?
Un programa es una secuencia de instrucciones que especican como ejecutar un computo. El computo puede ser matem atico, como solucionar un sistema de ecuaciones o determinar las ra ces de un polinomio, pero tambi en puede ser simbolico, como buscar y reemplazar el texto de un documento o (aunque parezca raro) compilar un programa. Las instrucciones (comandos, ordenes) tienen una apariencia diferente en lenguajes de programacion diferentes, pero existen algunas funciones b asicas que se presentan en casi todo lenguaje:
14
Entrada: recibir datos del teclado, o de un archivo o de otro aparato. Salida: mostrar datos en el monitor o enviar datos a un archivo u otro aparato. Matem aticas: ejecutar operaciones b asicas, como la adicion y la multiplicacion. Operacion condicional: probar la veracidad de alguna condicion y ejecutar una secuencia de instrucciones apropiada. Repeticion ejecutar alguna accion repetidas veces, usualmente con alguna variacion. Aunque sea dif cil de creer, todos los programas en existencia son formulados exclusivamente con tales instrucciones. As , una manera de describir la programacion es: el proceso de romper una tarea en tareas cada vez m as pequenas hasta que e stas sean lo sucientemente sencillas como para ser ejecutadas con una secuencia de estas instrucciones b asicas. Quiz as esta descripcion es un poco ambigua. No se preocupe. Explicaremos e sto con m as detalle en el tema de algoritmos.
15
16
de Linus fue un programa que intercambiar a la impresion de AAAA con BBBB. Este programa se convirtio en Linux (de The Linux Users Guide Version Beta 1). Otros cap tulos tratar an m as el tema de la depuracion y otras t ecnicas de programacion.
17
se ha analizado la oracion sint acticamente, se puede deducir el signicado, o la sem antica, de la oracion. Si usted sabe lo que es un zapato y el signicado de caer, comprender a el signicado de la oracion. Aunque existen muchas cosas en comun entre los lenguajes naturales y los formales por ejemplo las unidades, la estructura, la sint actica y la sem antica tambi en existen muchas diferencias. Ambiguedad: los lenguajes naturales tienen much simas ambiguedades que se superan usando claves contextuales e informacion adicional. Los lenguajes formales son disenados para estar completamente libres de ambiguedades o, tanto como sea posible, lo que quiere decir que cualquier sentencia tiene solo un signicado sin importar el contexto en el que se encuentra. Redundancia: para reducir la ambiguedad y los malentendidos, los lenguajes naturales utilizan bastante redundancia. Como resultado tienen una abundancia de posibilidades para expresarse. Los lenguajes formales son menos redundantes y mas concisos. Literalidad: los lenguajes naturales tienen muchas met aforas y frases comunes. El signicado de un dicho, por ejemplo: Estirar la pata, es diferente al signicado de sus sustantivos y verbos. En este ejemplo, la oracion no tiene nada que ver con una pata y signica morirse. En los lenguajes formales solo existe el signicado literal. Los que aprenden a hablar un lenguaje naturales decir todo el mundomuchas veces tienen dicultad en adaptarse a los lenguajes formales. A veces la diferencia entre los lenguajes formales y los naturales es comparable a la diferencia entre la prosa y la poes a: Poes a: se utiliza una palabra por su cualidad auditiva tanto como por su signicado. El poema, en su totalidad, produce un efecto o reaccion emocional. La ambiguedad no es solo comun, sino utilizada a proposito. Prosa: el signicado literal de la palabra es m as importante y la estructura contribuye m as al signicado. La prosa se presta m as al an alisis que la poes a, pero todav a contiene ambiguedad. Programa: el signicado de un programa es inequ voco y literal, y es entendido en su totalidad analizando las unidades y la estructura. He aqu unas sugerencias para la lectura de un programa (y de otros lenguajes formales). Primero, recuerde que los lenguajes formales son mucho m as densos que los lenguajes naturales y, por consecuencia, toma mas tiempo dominarlos. Adem as, la estructura es muy importante, entonces no es una buena idea leerlo
18
de pies a cabeza, de izquierda a derecha. En lugar de e sto, aprenda a separar las diferentes partes en su mente, identicar las unidades e interpretar la estructura. Finalmente, ponga atencion a los detalles. La fallas de puntuacion y la ortograf a afectar an negativamente la ejecucion de sus programas.
2.6. Glosario
Solucion de problemas: el proceso de formular un problema, hallar la solucion y expresarla. Lenguaje de alto nivel: un lenguaje como Python que es disenado para ser f acil de leer y escribir por la gente. Lenguaje de bajo nivel: un lenguaje de programacion que es disenado para ser f acil de ejecutar para una computadora; tambi en se lo llama lenguaje de m aquina o lenguaje ensamblador. Portabilidad: la cualidad de un programa que puede ser ejecutado en m as de un tipo de computadora. Interpretar: ejecutar un programa escrito en un lenguaje de alto nivel traduci endolo l nea por l nea. Compilar: traducir un programa escrito en un lenguaje de alto nivel a un lenguaje de bajo nivel de una vez, en preparacion para la ejecucion posterior.
2.6 Glosario
19
Codigo fuente: un programa escrito en un lenguaje de alto nivel antes de ser compilado. Codigo objeto: la salida del compilador una vez que el programa ha sido traducido. Programa ejecutable: otro nombre para el codigo de objeto que est a listo para ser ejecutado. Guion (script): un programa archivado (que va a ser interpretado). Programa: un grupo de instrucciones que especica un computo. Algoritmo: un proceso general para resolver una clase completa de problemas. Error (bug): un error en un programa. Depuracion: el proceso de hallazgo y eliminacion de los tres tipos de errores de programacion. Sintaxis: la estructura de un programa. Error sint actico: un error estructural que hace que un programa sea imposible de analizar sint acticamente (e imposible de interpretar). Error en tiempo de ejecucion: un error que no ocurre hasta que el programa ha comenzado a ejecutar e impide que el programa continue. Excepcion: otro nombre para un error en tiempo de ejecucion. Error sem antico: un error en un programa que hace que ejecute algo que no era lo deseado. Sem antica: el signicado de un programa. Lenguaje natural: cualquier lenguaje hablado que evoluciono de forma natural. Lenguaje formal: cualquier lenguaje disenado que tiene un proposito espec co, como la representacion de ideas matem aticas o programas de computadoras; todos los lenguajes de programacion son lenguajes formales. Unidad: uno de los elementos b asicos de la estructura sint actica de un programa, an alogo a una palabra en un lenguaje natural. An alisis sint actico: la revision de un programa y el an alisis de su estructura sint actica. Sentencia print: una instruccion que causa que el int erprete de Python muestre un valor en el monitor.
Cap tulo 3
Qu e ocurre con valores como "17" y "3.2"? Parecen numeros, pero est an encerrados entre comillas como las cadenas. >>> type("17") <type string> >>> type("3.2") <type string> Ellos son cadenas. Cuando usted digita un numero grande, podr a estar tentado a usar comas para separar grupos de tres d gitos, como en 1,000,000. Esto no es un numero entero legal en Python, pero esto si es legal: >>> print 1,000,000 1 0 0 Bueno, eso no es lo que esper abamos!. Resulta que 1,000,000 es una tupla, algo que encontraremos en el Cap tulo 10. De momento, recuerde no poner comas en sus numeros enteros.
3.2. Variables
Una de las caracter sticas m as poderosas en un lenguaje de programacion es la capacidad de manipular variables. Una variable es un nombre que se reere a un valor. La sentencia de asignacion crea nuevas variables y les da valores: >>> mensaje = "Qu e Onda?" >>> n = 17 >>> pi = 3.14159 Este ejemplo hace tres asignaciones: la primera asigna la cadena "Qu e Onda?" a una nueva variable denominada mensaje, la segunda le asigna el entero 17 a n y la tercera le asigna el numero de punto otante 3.14159 a pi. Una manera comun de representar variables en el papel es escribir el nombre de la variable con una echa apuntando a su valor. Esta clase de dibujo se denomina diagrama de estados porque muestra el estado de cada una de las variables (piense en los valores como el estado mental de las variables). Este diagrama muestra el resultado de las sentencias de asignacion anteriores:
23
mensaje n pi
La sentencia print tambi en funciona con variables. >>> print mensaje Que Onda? >>> print n 17 >>> print pi 3.14159 En cada caso el resultado es el valor de la variable. Las variables tambi en tienen tipos; nuevamente, le podemos preguntar al int erprete cuales son. >>> type(mensaje) <type string> >>> type(n) <type int> >>> type(pi) <type float> El tipo de una variable es el mismo del valor al que se reere.
24
>>> mas$ = 1000000 SyntaxError: invalid syntax >>> class = "introducci on a la programaci on" SyntaxError: invalid syntax 76trombones es ilegal porque no empieza con una letra. mas$ es ilegal porque contiene un car acter ilegal, el s mbolo $. Qu e sucede con class? Resulta que class es una de las palabras reservadas (keywords) de Python. Las palabras reservadas denen las reglas del lenguaje y su estructura, y no pueden ser usadas como nombres de variables. Python tiene veintiocho palabras reservadas: and assert break class raise continue def del elif return else except exec finally try for from global if while import in is lambda not or pass print
Usted puede mantener esta lista a mano. Si el int erprete se queja por alguno de sus nombres de variables, y usted no sabe por qu e, busquelo en esta lista.
3.4. Sentencias
Una sentencia es una instruccion que el int erprete de Python puede ejecutar. Hemos visto dos clases de sentencias: la asignacion y print. Cuando usted digita una sentencia en la l nea de comandos, Python la ejecuta y despliega el resultado, si hay alguno. El resultado de un print es un valor. Las asignaciones no producen un resultado. Un guion usualmente contiene una secuencia de sentencias. Si hay m as de una, los resultados aparecen uno a uno a medida que las sentencias se ejecutan. Por ejemplo, el guion print 1 x = 2 print x produce la salida 1 2 Observe nuevamente que la sentencia de asignacion no produce salida.
25
26
Los s mbolos +, -, y /, y los par entesis para agrupar, signican en Python lo mismo que en la matem atica. El asterisco (*) es el s mbolo para la multiplicacion, y ** es el s mbolo para la exponenciacion. Cuando el nombre de una variable aparece en lugar de un operando, se reemplaza por su valor antes de calcular la operacion La suma, resta, multiplicacion y exponenciacion realizan lo que usted esperar a, pero la division podr a sorprenderlo. La siguiente operacion tiene un resultado inesperado: >>> minuto = 59 >>> minuto/60 0 El valor de minuto es 59, y 59 dividido por 60 es 0.98333, no 0. La razon para esta discrepancia radica en que Python est a realizando division entera. Cuando los dos operandos son enteros el resultado tambi en debe ser un entero; y, por convencion, la division entera siempre redondea hacia abajo, incluso en casos donde el siguiente entero est a muy cerca. Una solucion posible a este problema consiste en calcular un porcentaje, en lugar de una fraccion: >>> minuto*100/60 98 De nuevo, el resultado se redondea; pero, al menos ahora, el resultado estar a mas aproximado. Otra alternativa es usar la division en punto otante, lo que haremos en el Cap tulo 4.
27
Los Par entesis tienen la precedencia m as alta y pueden usarse para forzar la evaluacion de una expresion de la manera que usted desee. Ya que las expresiones en par entesis se evaluan primero, 2 * (3-1) es 4, y (1+1)**(5-2) es 8. Usted tambi en puede usar par entesis para que una expresion quede m as legible, como en (minuto * 100) / 60, aunque esto no cambie el resultado. La Exponenciacion tiene la siguiente precedencia m as alta, as que 2**1+1 es 3 y no 4, y 3*1**3 es 3 y no 27. La Multiplicacion y la Division tienen la misma precedencia, aunque es m as alta que la de la Adicion y la Subtraccion, que tambi en tienen la misma precedencia. As que 2*3-1 da 5 en lugar de 4, y 2/3-1 es -1, no 1 (recuerde que en division entera, 2/3=0). Los operadores con la misma precedencia se evaluan de izquierda a derecha. Recordando que minuto=59, en la expresion minuto*100/60; la multiplicacion se hace primero, resultando 5900/60, lo que a su vez da 98. Si las operaciones se hubieran evaluado de derecha a izquierda, el resultado ser a 59/1, que es 59, y no es lo correcto.
Sin embargo, el operador + funciona con cadenas, aunque no calcula lo que usted esperar a. Para las cadenas, el operador + representa la concatenacion , que signica unir los dos operandos enlaz andolos en el orden en que aparecen. Por ejemplo: fruta = "banano" bienCocinada = " pan con nueces" print fruta + bienCocinada La salida de este programa es banano pan con nueces. El espacio antes de la palabra pan es parte de la cadena y sirve para producir el espacio entre las cadenas concatenadas. El operador * tambi en funciona con las cadenas; hace una repeticion. Por ejemplo, Fun*3 es FunFunFun. Uno de los operandos tiene que ser una cadena, el otro tiene que ser un entero.
28
Estas interpretaciones de + y * tienen sentido por la analog a con la suma y la multiplicacion. As como 4*3 es equivalente a 4+4+4, esperamos que "Fun"*3 sea lo mismo que "Fun"+"Fun"+"Fun", y lo e s. Sin embargo, las operaciones de concatenacion y repeticion sobre cadenas tienen una diferencia signicativa con las operaciones de suma y multiplicacion. Puede usted pensar en una propiedad que la suma y la multiplicacion tengan y que la concatenacion y repeticion no?
3.9. Composicion
Hasta aqu hemos considerado a los elementos de un programavariables, expresiones y sentenciasaisladamente, sin especicar como combinarlos. Una de las caracter sticas mas utiles de los lenguajes de programacion es su capacidad de tomar pequenos bloques para componer con ellos. Por ejemplo, ya que sabemos como sumar numeros y como imprimirlos; podemos hacer las dos cosas al mismo tiempo: >>> 20 print 17 + 3
De hecho, la suma tiene que calcularse antes que la impresion, as que las acciones no est an ocurriendo realmente al mismo tiempo. El punto es que cualquier expresion que tenga numeros, cadenas y variables puede ser usada en una sentencia de impresion (print). Usted ha visto un ejemplo de esto: print "N umero de minutos desde media noche: ", hora*60+minuto Usted tambi en puede poner expresiones arbitrarias en el lado derecho de una sentencia de asignacion: porcentaje = (minuto * 100) / 60 Esto no parece nada impresionante ahora, pero vamos a ver otros ejemplos en los que la composicion hace posible expresar c alculos complejos organizada y concisamente. Advertencia: hay restricciones sobre los lugares en los que se pueden usar las expresiones. Por ejemplo, el lado izquierdo de una asignacion tiene que ser un nombre de variable, no una expresion. As que esto es ilegal: minuto+1 = hora.
3.10 Comentarios
29
3.10. Comentarios
A medida que los programas se hacen m as grandes y complejos, se vuelven m as dif ciles de leer. Los lenguajes formales son densos; y, a menudo, es dif cil mirar una seccion de codigo y saber qu e hace, o por qu e lo hace. Por esta razon, es una muy buena idea anadir notas a sus programas para explicar, en lenguaje natural, lo que hacen. Estas notas se denominan comentarios y se marcan con el s mbolo #: # calcula el porcentaje de la hora que ha pasado porcentaje = (minuto * 100) / 60 En este caso, el comentario aparece en una l nea completa. Tambi en pueden ir comentarios al nal de una l nea: # precaucion: division entera porcentaje = (minute * 100) / 60 Todo lo que sigue desde el # hasta el n de la l nea se ignorano tiene efecto en el programa. El mensaje es para el programador que escribe el programa o para algun programador que podr a usar este codigo en el futuro. En este caso, le recuerda al lector el sorprendente comportamiento de la division entera en Python.
3.11. Glosario
Valor: un numero o una cadena (u otra cosa que se introduzca m as adelante) que puede ser almacenado en una variable o calculado en una expresion. Tipo: conjunto de valores. El tipo del valor determina como se puede usar en expresiones. Hasta aqu , los tipos que usted ha visto son enteros (tipo int), numeros de punto otante (tipo float) y cadenas (tipo string). Punto otante: formato para representar numeros con parte decimal. Variable: nombre que se reere a un valor. Sentencia: seccion de codigo que representa un comando o accion. Hasta aqu las sentencias que usted ha visto son la de asignacion y la de impresion. Asignacion: corresponde a la sentencia que pone un valor en una variable. Diagrama de estados: es la representacion gr aca de un conjunto de variables y los valores a los que se reeren.
30
Palabra reservada: es una palabra usada por el compilador para analizar sint acticamente un programa; usted no puede usar palabras reservadas como if, def, y while como nombres de variables. Operador: s mbolo especial que representa un simple c alculo como una suma, multiplicacion o concatenacion de cadenas. Operando: uno de los valores sobre el cual actua un operador. Expresion: combinacion de variables, operadores y valores que representa un uni co valor de resultado. Evaluar: simplicar una expresion ejecutando varias operaciones a n de retornar un valor unico. Division entera: operacion que divide un entero por otro y retorna un entero. La division entera retorna el numero de veces que el denominador cabe en el numerador y descarta el residuo. Reglas de precedencia: reglas que gobiernan el orden en que las expresiones que tienen multiples operadores y operandos se evaluan. Concatenar: unir dos operandos en el orden en que aparecen. Composicion: es la capacidad de combinar simples expresiones y sentencias dentro de sentencias y expresiones compuestas para representar c alculos complejos concisamente. Comentario: informacion que se incluye en un programa para otro programador (o lector del codigo fuente) que no tiene efecto en la ejecucion.
Cap tulo 4
Funciones
4.1. Llamadas a funciones
Usted ya ha visto un ejemplo de una llamada a funcion : >>> type("32") <type string> El nombre de la funcion es type, y despliega el tipo de un valor o variable. El valor o variable, que se denomina el argumento de la funcion, tiene que encerrarse entre par entesis. Es usual decir que una funcion toma un argumento y retorna un resultado. El resultado se denomina el valor de retorno. En lugar de imprimir el valor de retorno, podemos asignarlo a una variable: >>> betty = type("32") >>> print betty <type string> Otro ejemplo es la funcion id que toma un valor o una variable y retorna un entero que actua como un identicador unico: >>> id(3) 134882108 >>> betty = 3 >>> id(betty) 134882108 Cada valor tiene un id que es un numero unico relacionado con el lugar en la memoria en el que est a almacenado. El id de una variable es el id del valor al que la variable se reere.
32
Funciones
33
minuto/60, hace division entera, as que el resultado siempre es 0, incluso cuando han transcurrido 59 minutos. Una solucion es convertir minuto a punto otante para realizar la division en punto otante: >>> minuto = 59 >>> float(minute)/60.0 0.983333333333 Otra alternativa es sacar provecho de las reglas de conversion autom atica de tipos, que se denominan coercion de tipos. Para los operadores matem aticos, si algun operando es un numero flotante, el otro se convierte autom aticamente a flotante: >>> minuto = 59 >>> minuto / 60.0 0.983333333333 As que haciendo el denominador otante, forzamos a Python a realizar division en punto otante.
34 >>> decibel = math.log10 (17.0) >>> angulo = 1.5 >>> altura = math.sin(angulo)
Funciones
La primera sentencia le asigna a decibel el logaritmo de 17, en base 10. Tambi en hay una funcion llamada log que usa la base logar tmica e. La tercera sentencia encuentra el seno del valor de la variable angulo. sin y las otras funciones trigonom etricas (cos, tan, etc.) reciben sus argumentos en radianes. Para convertir de grados a radianes hay que dividir por 360 y multiplicar por 2*pi. Por ejemplo, para encontrar el seno de 45 grados, primero calculamos el a ngulo en radianes y luego tomamos el seno: >>> grados = 45 >>> angulo = grados * 2 * math.pi / 360.0 >>> math.sin(angulo) La constante pi tambi en hace parte del modulo matem atico. Si usted recuerda geometr a puede vericar el resultado compar andolo con la ra z cuadrada de 2 dividida por 2: >>> math.sqrt(2) / 2.0 0.707106781187
4.5. Composicion
As como las funciones matem aticas, las funciones de Python pueden componerse, de forma que una expresion sea parte de otra. Por ejemplo, usted puede usar cualquier expresion como argumento a una funcion: >>> x = math.cos(angulo + pi/2) Esta sentencia toma el valor de pi, lo divide por 2, y suma este resultado al valor de angulo. Despu es, la suma se le pasa como argumento a la funcion coseno (cos). Tambi en se puede tomar el resultado de una funcion y pasarlo como argumento a otra: >>> x = math.exp(math.log(10.0)) Esta sentencia halla el logaritmo en base e de 10 y luego eleva e a dicho resultado. El resultado se asigna a x.
35
36
Funciones
Note el espacio extra entre las dos l neas. Qu e pasa si deseamos m as espacio entre las l neas? Podemos llamar la misma funcion repetidamente: print "Primera L nea." nuevaL nea() nuevaL nea() nuevaL nea() print "Segunda L nea." O podemos escribir una nueva funcion llamada tresL neas que imprima tres l neas: def tresL neas(): nuevaL nea() nuevaL nea() nuevaL nea() nea." print "Primera L neas() tresL print "Segunda L nea." Esta funcion contiene tres sentencias, y todas est an sangradas por dos espacios. Como la proxima sentencia (print Primera L nea) no est a sangrada, Python la interpreta afuera de la funcion. Hay que enfatizar dos hechos sobre este programa: 1. Usted puede llamar la misma funcion repetidamente. De hecho, es una pr actica muy comun y util. neas 2. Usted puede llamar una funcion dentro de otra funcion; en este caso tresL nea. llama a nuevaL Hasta este punto, puede que no parezca claro porque hay que tomarse la molestia de crear todas estas funciones. De hecho, hay muchas razones, y este ejemplo muestra dos: Crear una nueva funcion le da a usted la oportunidad de nombrar un grupo de sentencias. Las funciones pueden simplicar un programa escondiendo un c alculo complejo detr as de un comando unico que usa palabras en lenguaje natural, en lugar de un codigo arcano. Crear una nueva funcion puede recortar el tamano de un programa eliminando el codigo repetitivo. Por ejemplo, una forma m as corta de imprimir nueve l neas consecutivas consiste en llamar la funcion tresL neas tres veces. Como ejercicio, escriba una funci on llamada nueveL neas que use a tresL neas para imprimir nueve l neas. Como imprimir a veintisiete l neas?
37
38
Funciones
adentro de otra. En este caso, la denicion interna no se ejecuta hasta que la otra funcion se llame. Las llamadas a funcion son como un desv o en el ujo de ejecucion. En lugar de continuar con la siguiente sentencia, el ujo salta a la primera l nea de la funcion llamada, ejecuta todas las sentencias internas, y regresa para continuar donde estaba previamente. Esto suena sencillo, hasta que tenemos en cuenta que una funcion puede llamar a otra. Mientras se est a ejecutando una funcion, el programa puede ejecutar las sentencias en otra funcion. Pero, mientras se est a ejecutando la nueva funcion, el programa puede tener que ejecutar otra funcion!. Afortunadamente, Python lleva la pista de donde est a elmente, as que cada vez que una funcion termina, el programa continua su ejecucion en el punto donde se la llamo. Cuando llega al n del programa, la ejecucion termina. Cual es la moraleja de esta sordida historia? Cuando lea un programa, no lo haga de arriba hacia abajo. En lugar de e sto, siga el ujo de ejecucion.
4.10 Las variables y los par ametros son locales >>> imprimaDoble(3.14159) 3.14159 3.14159
39
En el primer llamado de funcion el argumento es una cadena. En el segundo es un entero. En el tercero es un otante (float). Las mismas reglas de composicion que se aplican a las funciones primitivas, se aplican a las denidas por el programador, as que podemos usar cualquier clase de expresion como un argumento para imprimaDoble: >>> imprimaDoble(Spam*4) SpamSpamSpamSpam SpamSpamSpamSpam >>> imprimaDoble(math.cos(math.pi)) -1.0 -1.0 Como de costumbre, la expresion se evalua antes de que la funcion se ejecute as que imprimaDoble retorna SpamSpamSpamSpam SpamSpamSpamSpam en lugar de Spam*4 Spam*4. Como ejercicio, escriba una llamada a imprimaDoble que retorne Spam*4 Spam*4. Pista: las cadenas pueden encerrarse en comillas sencillas o dobles, y el tipo de la comilla que no se usa puede usarse adentro como parte de la cadena. Tambi en podemos usar una variable como argumento: >>> m = Oh, mundo cruel. >>> imprimaDoble(m) Oh, mundo cruel. Oh, mundo cruel. Observe algo muy importante, el nombre de la variable que pasamos como argumento (m) no tiene nada que ver con el nombre del par ametro (pedro). No importa como se nombraba el valor originalmente (en el lugar donde se hace el llamado); en la funcion imprimaDoble, la seguimos llamando de la misma manera pedro.
40
Funciones
Esta funcion toma dos argumentos, los concatena, y luego imprime el resultado dos veces. Podemos llamar a la funcion con dos cadenas: >>> >>> >>> Pie eis cantar1 = "Pie Jesu domine, " cantar2 = "Dona eis requiem." cocatenarDoble(cantar1, cantar2) Jesu domine, Dona eis requiem. Pie Jesu domine, Dona requiem.
Cuando concatenarDoble termina, la variable cat se destruye. Si intentaramos imprimirla obtendr amos un error: >>> print cat NameError: cat Los par ametros tambi en son locales. Por ejemplo, afuera de la funcion imprimaDoble, no existe algo como pedro. Si usted intenta usarlo Python se quejar a.
41
El orden de la pila muestra el ujo de ejecucion. imprimaDoble fue llamada por concatenarDoble, y concatenarDoble fue llamada por main , que es un nombre especial para la funcion m as superior (la principal, que tiene todo programa). Cuando usted crea una variable afuera de cualquier funcion, pertenece a main . Cada par ametro se reere al mismo valor que su argumento correspondiente. As que parte1 tiene el mismo valor que cantar1, parte2 tiene el mismo valor que cantar2, y pedro tiene el mismo valor que cat. Si hay un error durante una llamada de funcion, Python imprime el nombre de e sta, el nombre de la funcion que la llamo, y as sucesivamente hasta llegar a main . Por ejemplo, si intentamos acceder a cat desde imprimaDoble, obtenemos un error de nombre (NameError): Traceback (innermost last): File "test.py", line 13, in __main__ concatenarDoble(cantar1, cantar2) File "test.py", line 5, in concatenarDoble imprimaDoble(cat) File "test.py", line 9, in imprimaDoble print cat NameError: cat Esta lista de funciones se denomina un trazado inverso. Nos informa en qu e archivo de programa ocurrio el error, en qu e l nea, y qu e funciones se estaban ejecutando en ese momento. Tambi en muestra la l nea de codigo que causo el error. Note la similaridad entre el trazado inverso y el diagrama de pila. Esto no es una coincidencia.
42
Funciones
La respuesta a la tercera pregunta es armativa y lo lograremos en el cap tulo 5. Como ejercicio, responda las dos primeras preguntas intent andolas en Python. (Cuando usted se est e preguntando si algo es legal o ilegal una buena forma de averiguarlo es intentarlo en el int erprete).
4.13. Glosario
Llamada a funcion: sentencia que ejecuta una funcion. Consiste en el nombre de la funcion seguido por una lista de argumentos encerrados entre par entesis. Argumento: valor que se le da a una funcion cuando se la est a llamando. Este valor se le asigna al par ametro correspondiente en la funcion. Valor de retorno: es el resultado de una funcion. Si una llamada a funcion se usa como una expresion, el valor de e sta es el valor de retorno de la funcion. Conversion de tipo: sentencia expl cita que toma un valor de un tipo y calcula el valor correspondiente de otro tipo. Coercion de tipos: conversion de tipo que se hace autom aticamente de acuerdo a las reglas de coercion del lenguaje de programacion. Modulo: archivo que contiene una coleccion de funciones y clases relacionadas. Notacion punto: sintaxis para llamar una funcion que se encuentra en otro modu lo, especicando el nombre modulo seguido por un punto y el nombre de la funcion (sin dejar espacios intermedios). Funcion: es la secuencia de sentencias que ejecuta alguna operacion util y que tiene un nombre denido. Las funciones pueden tomar o no tomar par ametros y pueden entregar o no entregar un resultado. Denicion de funcion: sentencia que crea una nueva funcion especicando su nombre, par ametros y las sentencias que ejecuta. Flujo de ejecucion: orden en el que las sentencias se ejecutan cuando un programa corre. Par ametro: nombre usado dentro de una funcion para referirse al valor que se pasa como argumento. Variable local: variable denida dentro de una funcion. Una variable local solo puede usarse dentro de su funcion.
4.13 Glosario
43
Diagrama de pila: es la representacion gr aca de una pila de funciones, sus variables, y los valores a los que se reeren. Marco: una caja en un diagrama de pila que representa un llamado de funcion. Contiene las variables locales y los par ametros de la funcion. Trazado inverso: lista de las funciones que se estaban ejecutando y que se imprime cuando ocurre un error en tiempo de ejecucion.
Cap tulo 5
Condicionales y recursion
5.1. El operador residuo
El operador residuo trabaja con enteros (y expresiones enteras) calculando el residuo del primer operando cuando se divide por el segundo. En Python este operador es un signo porcentaje ( %). La sintaxis es la misma que para los otros operadores: >>> >>> 2 >>> >>> 1 cociente = 7 / 3 print cociente residuo = 7 % 3 print residuo
As que 7 dividido por 3 da 2 con residuo 1. El operador residuo resulta ser sorprendentemente util. Por ejemplo, usted puede chequear si un numero es divisible por otro si x %y es cero, entonces x es divisible por y. Usted tambi en puede extraer el d gito o d gitos m as a la derecha de un numero. Por ejemplo, x % 10 entrega el d gito m as a la derecha de x (en base 10). Igualmente, x % 100 entrega los dos ultimos d gitos.
46
Condicionales y recursion
Solo hay dos valores booleanos: True (cierto) y False (falso). Las mayusculas importan, ya que true y false no son valores booleanos. El operador == compara dos valores y produce una expresion booleana: >>> 5 == 5 True >>> 5 == 6 False En la primera sentencia, los dos operandos son iguales, as que la expresion evalua a True (cierto); en la segunda sentencia, 5 no es igual a 6, as que obtenemos False (falso). El operador == es uno de los operadores de comparacion ; los otros son: x x x x x != y > y < y >= y <= y # # # # # x x x x x no es es es es es igual y mayor que y menor que y mayor o igual a y menor o igual a y
Aunque estas operaciones probablemente son familiares para usted, los s mbolos en Python dieren de los matem aticos. Un error comun consiste en usar un solo signo igual (=) en lugar en un doble signo igual (==). Recuerde que = es el operador para la asignacion y que == es el operador para comparacion. Tenga en cuenta que no existen los signos =< o =>.
47
En general, esto no se considera un buen estilo de programacion. Si usted desea comparar un valor con cero, procure codicarlo expl citamente.
Condicionales y recursion
Si el residuo de dividir x por 2 es 0, entonces sabemos que x es par, y el programa despliega un mensaje anunciando esto. Si la condicion es falsa, la segunda sentencia se ejecuta. Como la condicion, que es una expresion booleana, debe ser cierta o falsa, exactamente una de las alternativas se va a ejecutar. Estas alternativas se denominan ramas, porque, de hecho, son ramas en el ujo de ejecucion. Y endonos por las ramas, si usted necesita chequear la paridad (si un numero es par o impar) a menudo, se podr a envolver el codigo anterior en una funcion: def imprimirParidad(x): if x%2 == 0: print x, "es par" else: print x, "es impar" Para cualquier valor de x, imprimirParidad despliega un mensaje apropiado. Cuando se llama la funcion, se le puede pasar cualquier expresion entera como argumento. >>> imprimirParidad(17) >>> imprimirParidad(y+1)
5.7 Condicionales anidados funcionB() elif eleccion == C: funcionC() else: print "Eleccion incorrecta."
49
Cada condicion se chequea en orden. Si la primera es falsa, se chequea la siguiente, y as sucesivamente. Si una de ellas es cierta, se ejecuta la rama correspondiente y la sentencia termina. Si hay m as de una condicion cierta, solo la primera rama que evalua a cierto se ejecuta. Como ejercicio, envuelva estos ejemplos en funciones llamadas comparar(x,y) y despachar(eleccion).
50
Condicionales y recursion
La sentencia print se ejecuta solamente si el ujo de ejecucion ha pasado las dos condiciones, as que podemos usar el operador and: if 0 < x and x < 10: print "x es un digito positivo." Esta clase de condiciones es muy comun, por esta razon Python proporciona una sintaxis alternativa que es similar a la notacion matem atica: if 0 < x < 10: print "x es un digito positivo" Desde el punto de vista sem antico e sta condicion es la misma que la expresion compuesta y que el condicional anidado.
La funcion imprimirLogaritmo toma un par ametro denominado x. Lo primero que hace es chequear si x es menor o igual a 0, caso en el que despliega un mensaje de error y luego usa a return para salir de la funcion. El ujo de ejecucion inmediatamente retorna al punto donde se hab a llamado la funcion, y las l neas restantes de la funcion no se ejecutan. Recuerde que para usar una funcion del modulo matem atico (math) hay que importarlo previamente.
5.9. Recursion
Hemos mencionado que es legal que una funcion llame a otra, y usted ha visto varios ejemplos as . Hemos olvidado mencionar el hecho de que una funcion tambi en puede llamarse a s misma. Al principio no parece algo util, pero resulta ser
5.9 Recursion
51
una de las capacidades m as interesantes y m agicas que un programa puede tener. Por ejemplo, observe la siguiente funcion: def conteo(n): if n == 0: print "Despegue!" else: print n conteo(n-1) conteo espera que el par ametro n sea un numero entero positivo. Si n es 0, despliega la cadena, Despegue!. Si no lo es, despliega n y luego llama a la funcion llamada conteoella mismapasando a n-1 como argumento. Analizemos lo que sucede si llamamos a esta funcion as : >>> conteo(3) La ejecucion de conteo comienza con n=3, y como n no es 0, despliega el valor 3, y se llama a s misma ... La ejecucion de conteo comienza con n=2, y como n no es 0, despliega el valor 2, y se llama a si misma ... La ejecucion de conteo comienza con n=1, y como n no es 0, despliega el valor 1, y se llama a s misma ... La ejecucion de conteo comienza con n=0, y como n es 0, despliega la cadena Despegue! y retorna (naliza). El conteo que recibio n=1 retorna. El conteo que recibio n=2 retorna. El conteo que recibio n=3 retorna. Y el ujo de ejecucion regresa a main (vaya viaje!). As que, la salida total luce as : 3 2 1 Despegue! Como otro ejemplo, utilizaremos nuevamente las funciones nuevaL nea y tresL neas:
52 def nuevalinea(): print def tresL neas(): nuevaL nea() nuevaL nea() nuevaL nea()
Condicionales y recursion
Este trabajo no ser a de mucha ayuda si quisi eramos desplegar 2 l neas o 106. Una mejor alternativa ser a:
Esta funcion es similar a conteo; en tanto n sea mayor a 0, despliega una nueva l nea y luego se llama a s misma para desplegar n-1 l neas adicionales. As , el numero total de nuevas l neas es 1 + (n - 1) que, si usted verica con a lgebra, resulta ser n. El proceso por el cual una funcion se llama a s misma es la recursion , y se dice que estas funciones son recursivas.
53
Como siempre, el tope de la pila es el marco para main . Est a vac o porque no ametros. creamos ninguna variable en main ni le pasamos par Los cuatro marcos de conteo tienen diferentes valores para el par ametro n. El fondo de la pila, donde n=0, se denomina el caso base . Como no hace una llamada recursiva, no hay mas marcos. Como ejercicio, dibuje un diagrama de pila para nL neas llamada con n=4.
54
Condicionales y recursion
Antes de llamar a raw input es una muy buena idea desplegar un mensaje dici endole al usuario qu e digitar. Este mensaje se denomina indicador de entrada (prompt en ingl es). Podemos dar un argumento prompt a raw input: >>> nombre = raw_input ("Cual es tu nombre? ") Cual es tu nombre? Arturo, Rey de los Bretones! >>> print nombre Arturo, Rey de los Bretones! Si esperamos que la respuesta sea un entero, podemos usar la funcion input: prompt = "Cual es la velocidad de una golondrina sin carga?\n" velocidad = input(prompt) Si el usuario digita una cadena de d gitos, e stos se convierten a un entero que se asigna a velocidad. Desafortunadamente, si el usuario digita un car acter que no sea un d gito, el programa se aborta: >>> velocidad = input (prompt) prompt = "Cual es la velocidad una golondrina sin carga?\n" Que quiere decir, una golondria Africana o Europea? SyntaxError: invalid syntax Para evitar este error, es una buena idea usar raw input para obtener una cadena y las funciones de conversion para transformarla en otros tipos.
5.13 Glosario
55
5.13. Glosario
Operador residuo: operador que se denota con un signo porcentaje ( %), y trabaja sobre enteros produciendo el residuo de un numero al dividirlo por otro. Expresion booleana: expresion cierta o falsa. Operador de comparacion: uno de los operadores que compara dos valores: ==, !=, >, <, >=, y <=. Operador logico: uno de los operadores que combina expresiones booleanas: and, or, y not. Sentencia condicional: sentencia que controla el ujo de ejecucion dependiendo de alguna condicion. Condicion: la expresion booleana en una sentencia condicional que determina que rama se ejecuta. Sentencia compuesta: es la sentencia que comprende una cabecera y un cuerpo. La cabecera termina con dos puntos seguidos (:). El cuerpo se sangra o indenta con respecto a la cabecera. Bloque: grupo de sentencias consecutivas con la misma indentacion. Cuerpo: el bloque, en una sentencia compuesta, que va despu es de la cabecera. Anidamiento: situacion en la que hay una estructura dentro de otra, tal como una sentencia condicional dentro de una rama de otra sentencia condicional. Recursion: es el proceso de llamar la funcion que se est a ejecutando actualmente. Caso base: corresponde a una rama de la sentencia condicional dentro de una funcion recursiva, que no hace un llamado recursivo. Recursion innita: funcion que se llama a s misma recursivamente sin alcanzar nunca el caso base. En Python una recursion innita eventualmente causa un error en tiempo de ejecucion. Prompt (indicador de entrada): una pista visual que le indica al usuario que digite alguna informacion.
Cap tulo 6
58
un valor de retorno. La expresion proporcionada puede ser arbitrariamente compleja, as que podr amos escribir esta funcion m as concisamente: def area(radio): return math.pi * radio**2 Por otro lado, las variables temporales, como temp, a menudo permiten depurar los programas m as f acilmente. Algunas veces es muy util tener multiples sentencias return, ubicadas en ramas distintas de un condicional: def valorAbsoluto(x): if x < 0: return -x else: return x Ya que estas sentencias return est an en un condicional alternativo, solo una ser a ejecutada. Tan pronto como esto suceda, la funcion termina sin ejecutar las sentencias que siguen. El codigo que aparece despu es de la sentencia return, o en un lugar que el ujo de ejecucion nunca puede alcanzar, se denomina codigo muerto. En una funcion fruct fera es una buena idea garantizar que toda ruta posible de ejecucion del programa llegue a una sentencia return. Por ejemplo: def valorAbsoluto(x): if x < 0: return -x elif x > 0: return x Este programa no es correcto porque si x llega a ser 0, ninguna condicion es cierta y la funcion puede terminar sin alcanzar una sentencia return. En este caso el valor de retorno que Python entrega es un valor especial denominado None: >>> print valorAbsoluto(0) None
Como ejercicio, escriba una funci on comparar que retorne 1 si x>y, 0 si x==y, y -1 si x<y.
59
El primer paso es considerar como lucir a la funcion distancia en Python. En otras palabras, cuales son las entradas (par ametros) y cual es la salida (valor de retorno)? En este caso, los dos puntos son las entradas, que podemos representar usando cuatro par ametros. El valor de retorno es la distancia, que es un valor de punto otante. Ya podemos escribir un borrador de la funcion: def distancia(x1, y1, x2, y2): return 0.0 Obviamente, esta version de la funcion no calcula distancias; siempre retorna cero. Pero es correcta sint acticamente y puede correr, lo que implica que la podemos probar antes de que la hagamos m as compleja. Para probar la nueva funcion la llamamos con valores simples: >>> distancia(1, 2, 4, 6) 0.0 Escogemos estos valores de forma que la distancia horizontal sea 3 y la vertical 4; de esta forma el resultado es 5 (la hipotenusa de un tri angulo con medidas 34-5). Cuando probamos una funcion es fundamental conocer algunas respuestas correctas. En este punto hemos conrmado que la funcion est a bien sint acticamente, y que podemos empezar a agregar l neas de codigo. Despu es de cada cambio, probamos la funcion otra vez. Si hay un error, sabemos donde debe estar en la ultima l nea que agregamos.
60
Un primer paso logico en este computo es encontrar las diferencias x2 x1 y y2 y1 . Almacenaremos estos valores en variables temporales llamadas dx y dy y los imprimiremos. def distancia(x1, y1, x2, y2): dx = x2 - x1 dy = y2 - y1 print "dx es", dx print "dy es", dy return 0.0 Si la funcion trabaja bien, las salidas deben ser 3 y 4. Si es as , sabemos que la funcion est a obteniendo los par ametros correctos y calculando el primer paso correctamente. Si no ocurre e sto, entonces hay unas pocas l neas para chequear. Ahora calculamos la suma de los cuadrados de dx y dy: def distancia(x1, y1, x2, y2): dx = x2 - x1 dy = y2 - y1 discuadrado = dx**2 + dy**2 print "discuadrado es: ", discuadrado return 0.0 Note que hemos eliminado las sentencias print que ten amos en el paso anterior. Este codigo se denomina andamiaje porque es util para construir el programa pero no hace parte del producto nal. De nuevo, corremos el programa y chequeamos la salida (que debe ser 25). Finalmente, si importamos el modulo math, podemos usar la funcion sqrt para calcular y retornar el resultado: def distancia(x1, y1, x2, y2): dx = x2 - x1 dy = y2 - y1 discuadrado = dx**2 + dy**2 resultado = math.sqrt(discuadrado) return resultado Si esto funciona bien, usted ha terminado. Si no, se podr a imprimir el valor de resultado antes de la sentencia return. Recapitulando, para empezar, usted deber a agregar solamente una l nea o dos cada vez. A medida que gane m as experiencia podr a escribir y depurar trozos mayores. De cualquier forma el proceso de desarrollo incremental puede evitarle mucho tiempo de depuracion. Los aspectos claves del proceso son:
6.3 Composicion
61
1. Empezar con un programa correcto y hacer pequenos cambios incrementales. Si en cualquier punto hay un error, usted sabr a exactamente donde est a. 2. Use variables temporales para almacenar valores intermedios de manera que se puedan imprimir y chequear. 3. Ya que el programa est e corriendo, usted puede remover parte del andamiaje o consolidar multiples sentencias en expresiones compuestas, pero solo si e sto no diculta la lectura del programa. Como ejercicio, desarrolle incrementalmente una funci on llamada hipotenusa, que retorne la longitud de la hipotenusa de un tri angulo rect angulo dadas las longitudes de los dos catetos como par ametros. Guarde cada etapa del proceso de desarrollo a medida que avanza.
6.3. Composicion
Como usted esperar a, se puede llamar una funcion fruct fera desde otra. Esta capacidad es la composicion . Como ejemplo vamos a escribir una funcion que toma dos puntos: el centro de un c rculo y un punto en el per metro, y que calcule el a rea total del c rculo. Asuma que el punto central est a almacenado en las variables xc y yc, y que el punto perimetral est a en xp y yp. El primer paso es encontrar el radio del c rculo, que es la distancia entre los dos puntos. Afortunadamente, hay una funcion, distancia, que hace eso: radio = distancia(xc, yc, xp, yp) El segundo paso es encontrar el a rea de un c rculo con dicho radio y retornarla: resultado = area(radio) return resultado Envolviendo todo en una funcion obtenemos: def area2(xc, yc, xp, yp): radio = distancia(xc, yc, xp, yp) resultado = area(radio) return resultado Llamamos a esta funcion area2 para distinguirla de la funcion area denida previamente. Solo puede haber una funcion con un nombre dado dentro de un modulo.
62
Las variables temporales radio y area son utiles para desarrollar y depurar, pero una vez que el programa est a funcionando podemos hacer la funcion m as concisa componiendo las llamadas a funciones: def area2(xc, yc, xp, yp): return area(distancia(xc, yc, xp, yp)) Como ejercicio, escriba una funci on pendiente(x1, y1, x2, y2) que retorna la pendiente de una l nea que pasa por los puntos (x1, y1) y (x2, y2). Ahora, use esta funci on dentro de una funci on llamada interceptar(x1, y1, x2, y2) que retorna la intercepci on con el eje y de la l nea que pasa por los puntos (x1, y1) y (x2, y2).
6.5 M as recursion Las funciones booleanas se usan a menudo en las sentencias condicionales: if esDivisible(x, y): print "x es divisible por y" else: print "x no es divisible por y" Puede parecer tentador escribir algo como: if esDivisible(x, y) == True: Pero la comparacion extra es innecesaria. Como ejercicio escriba una funci on estaEntre(x, y, z) que retorne True si y x z o False si no ocurre esto.
63
6.5. M as recursion
Hasta aqu , usted solo ha aprendido un pequeno subconjunto de Python, pero podr a interesarle saber que este subconjunto es un lenguaje de programacion completo, lo que quiere decir que cualquier cosa que pueda ser calculada puede ser expresada en este subconjunto. Cualquier programa escrito alguna vez puede ser reescrito usando solamente las caracter sticas que usted ha aprendido hasta ahora (de hecho, necesitar a algunos comandos mas para manejar dispositivos como el teclado, el raton, los discos, etc., pero eso ser a todo). Demostrar esta armacion no es un ejercicio trivial y fue logrado por Alan Turing, uno de los primeros cient cos de la computacion (algunos dir an que el era un matem atico, pero la mayor a de los cient cos pioneros de la computacion eran matem aticos). Esto se conoce como la Tesis de Turing. Si usted toma un curso de Teor a de la Computacion tendr a la oportunidad de ver la demostracion. Para darle una idea de lo que puede hacer con las herramientas que ha aprendido, vamos a evaluar unas pocas funciones matem aticas denidas recursivamente. Una denicion recursiva es similar a una circular, ya que e stas contienen una referencia al concepto que se pretende denir. Una denicion circular verdadera no es muy util: frabjuoso: un adjetivo usado para describir algo que es frabjuoso. Si usted viera dicha denicion en el diccionario, quedar a confundido. Por otro lado, si encontrara la denicion de la funcion factorial hallar a algo como esto: 0! = 1 n! = n(n 1)! Esta denicion dice que el factorial de 0 es 1, y que el factorial de cualquier otro valor, n, es n multiplicado por el factorial de n 1.
64
As que 3! es 3 veces 2!, que es 2 veces 1!, que es 1 vez 0!. Juntando todo esto, 3! es igual a 3 veces 2 veces 1 vez 1, lo que da 6. Si usted puede escribir una denicion recursiva de algo, usualmente podr a escribir un programa para evaluarlo. El primer paso es decidir cuales son los par ametros para esta funcion. Con un poco de esfuerzo usted concluir a que factorial recibe un unico par ametro: def factorial(n): Si el argumento es 0, todo lo que hacemos es retornar 1: def factorial(n): if n == 0: return 1 Sino, y e sta es la parte interesante, tenemos que hacer una llamada recursiva para encontrar el factorial de n 1 y, entonces, multiplicarlo por n: def factorial(n): if n == 0: return 1 else: recur = factorial(n-1) da = n * recur return da El ujo de ejecucion de este programa es similar al ujo de conteo en la Seccion 5.9. Si llamamos a factorial con el valor 3: Como 3 no es 0, tomamos la segunda rama y calculamos el factorial de n-1... Como 2 no es 0, tomamos la segunda rama y calculamos el factorial de n-1... Como 1 no es 0, tomamos la segunda rama y calculamos el factorial de n-1... Como 0 es 0, tomamos la primera rama y retornamos 1 sin hacer m as llamados recursivos. El valor de retorno (1) se multiplica por n, que es 1, y el resultado se retorna. El valor de retorno (1) se multiplica por n, que es 2, y el resultado se retorna.
6.6 El salto de fe
65
El valor de retorno (2) se multiplica por n, que es 3, y el resultado, 6, se convierte en el valor de retorno del llamado de funcion que empezo todo el proceso. As queda el diagrama de pila para esta secuencia de llamados de funcion:
__main__ 6 factorial factorial factorial factorial n n n n 3 2 1 0 recur recur recur 2 1 1 da da da 6 2 2 1 1 1
Los valores de retorno mostrados se pasan hacia arriba a trav es de la pila. En cada marco, el valor de retorno es el valor de da, que es el producto de n y recur. Observe que en el ultimo marco, las variables locales recur y da no existen porque la rama que las crea no se ejecuto.
6.6. El salto de fe
Seguir el ujo de ejecucion es una forma de leer programas, pero r apidamente puede tornarse algo laber ntico. Una alternativa es lo que denominamos hacer el salto de fe. Cuando usted llega a un llamado de funcion, en lugar de seguir el ujo de ejecucion, se asume que la funcion trabaja correctamente y retorna el valor apropiado. De hecho, usted ya est a haciendo el salto de fe cuando usa las funciones primitivas. Cuando llama a math.cos o a math.exp, no est a examinando las implementaciones de estas funciones. Usted solo asume que est an correctas porque los que escribieron el modulo math son buenos programadores. Lo mismo se cumple para una de sus propias funciones. Por ejemplo, en la Seccion 6.4, escribimos una funcion llamada esDivisible que determina si un nume ro es divisible por otro. Una vez que nos hemos convencido de que esta funcion es correcta prob andola y examinando el codigopodemos usarla sin mirar el codigo nuevamente. Lo mismo vale para los programas recursivos. Cuando usted llega a una llamada recursiva, en lugar de seguir el ujo de ejecucion, deber a asumir que el llamado
66
recursivo funciona (retorna el resultado correcto) y luego preguntarse, Asumiendo que puedo encontrar el factorial de n 1, puedo calcular el factorial de n? En este caso, es claro que se puede lograr, multiplic andolo por n. Por supuesto que es un poco raro asumir que la funcion trabaja correctamente cuando ni siquiera hemos terminado de escribirla, por eso es que denominamos a esto el salto de fe!.
6.7. Un ejemplo m as
En el ejemplo anterior us abamos variables temporales para desplegar los pasos y depurar el codigo m as f acilmente, pero podr amos ahorrar unas cuantas l neas: def factorial(n): if n == 0: return 1 else: return n * factorial(n-1) Desde ahora, vamos a usar esta forma m as compacta, pero le recomendamos que use la forma m as expl cita mientras desarrolla las funciones. Cuando est en terminadas y funcionando, con un poco de inspiracion se pueden compactar. Despu es de factorial, el ejemplo m as comun de funcion matem atica, denida recursivamente, es la serie de fibonacci, que tiene la siguiente denicion: f ibonacci(0) = 1 f ibonacci(1) = 1 f ibonacci(n) = f ibonacci(n 1) + f ibonacci(n 2); Traducida a Python, luce as : def fibonacci (n): if n == 0 or n == 1: return 1 else: return fibonacci(n-1) + fibonacci(n-2) Si usted intenta seguir el ujo de ejecucion de bonacci, incluso para valores pequenos de n, le va a doler la cabeza. Pero, si seguimos el salto de fe, si asumimos que los dos llamados recursivos funcionan correctamente, es claro que el resultado correcto es la suma de e stos dos.
67
68
Este programa demuestra el uso de un patron denominado guarda. Los primeros dos condicionales actuan como guardas, protegiendo al codigo interno de los valores que pueden causar un error. Las guardas hacen posible demostrar que el codigo es correcto.
6.9. Glosario
Funcion fruct fera: funcion que retorna un resultado. Valor de retorno: el valor que entrega como resultado un llamado de funcion. Variable temporal: variable usada para almacenar un valor intermedio en un c alculo complejo. Codigo muerto: parte de un programa que nunca puede ser ejecutada, a menudo porque aparece despu es de una sentencia return. None: valor especial en Python retornado por las funciones que no tienen una sentencia return, o que tienen una sentencia return sin un argumento. Desarrollo incremental: un plan de desarrollo de programas que evita la depuracion, agregando y probando solo pequenas porciones de codigo en cada momento. Andamiaje: codigo que se usa durante el desarrollo de programas, pero no hace parte de la solucion nal. Guarda: una condicion que chequea y controla circunstancias que pueden causar errores.
Cap tulo 7
Iteracion
7.1. Asignacion multiple
Puede que usted ya haya descubierto que es posible realizar m as de una asignacion a la misma variable. Una nueva asignacion hace que la variable existente se reera a un nuevo valor (y deje de referirse al viejo valor). pedro print pedro print = 5 pedro, = 7 pedro
La salida de este programa es 5 7, porque la primera vez que pedro se imprime, tiene el valor 5, y la segunda vez tiene el valor 7. La coma al nal del primer print suprime la nueva l nea que tradicionalmente introduce Python despu es de los datos, por esta razon las dos salidas aparecen en la misma l nea. Aqu se puede ver como luce la asignacion multiple en un diagrama de estado:
pedro 5 7
Con asignacion multiple es muy importante distinguir entre una asignacion y una igualdad. Como Python usa el signo igual (=) para la asignacion podemos caer en la tentacion de interpretar a una sentencia como a = b como si fuera una igualdad. Y no lo es! Primero, la igualdad es conmutativa y la asignacion no lo es. Por ejemplo, en la matem atica si a = 7 entonces 7 = a. Pero en Python, la sentencia a = 7 es legal aunque 7 = a no lo es.
70
Iteracion
Adem as, en matem atica, una sentencia de igualdad siempre es cierta. Si a = b ahora, entonces a siempre ser a igual a b. En Python, una sentencia de asignacion puede lograr que dos variables sean iguales pero solo por un tiempo determinado: a = 5 b = a a = 3 # a y b ahora son iguales # a y b no son iguales ahora
La tercera l nea cambia el valor de a pero no cambia el valor de b, as que ya no ser an iguales. En algunos lenguajes de programacion se usa un signo diferente para la asignacion como <- o := para evitar la confusion. Aunque la asignacion multiple puede ser util se debe usar con precaucion. Si los valores de las variables cambian frecuentemente se puede dicultar la lectura y la depuracion del codigo.
71
El cuerpo comprende todas las sentencias bajo la cabecera que tienen la misma indentacion. Este ujo se denomina ciclo porque el tercer paso da la vuelta hacia el primero. Note que si la condicion es falsa la primera vez que se entra al while, las sentencias internas nunca se ejecutan. El cuerpo del ciclo deber a cambiar el valor de una o m as variables, de forma que la condicion se haga falsa en algun momento y el ciclo termine. De otra forma, el ciclo se repetir a para siempre, obteniendo un ciclo innito. Una broma comun entre los cient cos de la computacion es interpretar las instrucciones de los champus, Aplique champu, aplique rinse, repita, como un ciclo innito. En el caso de conteo, podemos probar que el ciclo termina porque sabemos que el valor de n es nito, y podemos ver que va haci endose m as pequeno cada vez que el while itera (da la vuelta), as que eventualmente llegaremos a 0. En otros casos esto no es tan f acil de asegurar: def secuencia(n): while n != 1: print n, if n%2 == 0: n = n/2 else: n = n*3+1
# n es par # n es impar
La condicion para este ciclo es n != 1, as que se repetir a hasta que n sea 1, lo que har a que la condicion sea falsa. En cada iteracion del ciclo while el programa despliega el valor de n y luego chequea si es par o impar. Si es par, el valor de n se divide por 2. Si es impar el valor se reemplaza por n*3+1. Si el valor inicial (del argumento) es 3, la secuencia que resulta es 3, 10, 5, 16, 8, 4, 2, 1. Como n aumenta algunas veces y otras disminuye, no hay una demostracion obvia de que n llegar a a ser 1, o de que el programa termina. Para algunos valores particulares de n podemos demostrar la terminacion. Por ejemplo, si el valor inicial es una potencia de dos, entonces el valor de n ser a par en cada iteracion del ciclo hasta llegar a 1. El ejemplo anterior termina con una secuencia as que empieza con 16. Dejando los valores particulares de lado, la interesante pregunta que nos planteamos es si podemos demostrar que este programa termina para todos los valores de n. Hasta ahora, nadie ha sido capaz de probarlo o refutarlo!. Como ejericio, reescriba la funci on nLineas de la Secci on 5.9, usando iteraci on en vez de recursi on.
72
Iteracion
7.3. Tablas
Una gama de aplicaciones, donde los ciclos se destacan, es la generacion de informacion tabular. Antes de que los computadores existieran la gente ten a que calcular logaritmos, senos, cosenos y otras funciones matem aticas a mano. Para facilitar la tarea, los libros matem aticos inclu an largas tablas con los valores de dichas funciones. La creacion de las tablas era un proceso lento y aburridor, y tend an a quedar con muchos errores. Cuando los computadores entraron en escena, una de las reacciones iniciales fue Esto es maravilloso! Podemos usar los computadores para generar las tablas, de forma que no habr an errores. Eso resulto (casi) cierto, pero poco prospectivo. Poco despu es los computadores y las calculadoras se hicieron tan ubicuos que las tablas se hicieron obsoletas. Bueno, casi. Para algunas operaciones los computadores usan tablas de valores para obtener una respuesta aproximada y luego hacer mas c alculos para mejorar la aproximacion. En algunos casos, se han encontrado errores en las tablas subyacentes, el m as famoso ha sido el de la tabla para realizar la division en punto otante en los procesadores Pentium de la compan a Intel. Aunque una tabla logar tmica no es tan util como en el pasado, todav a sirve como un buen ejemplo de iteracion. El siguiente programa despliega una secuencia de valores en la columna izquierda y sus logaritmos en la columna derecha: x = 1.0 while x < 10.0: print x, \t, math.log(x) x = x + 1.0 La cadena \t representa un car acter tab (tabulador). A medida que los car acteres y las cadenas se despliegan en la pantalla un marcador invisible denominado cursor lleva pista de donde va a ir el siguiente car acter. Despu es de una sentencia print, el cursor va al comienzo de la siguiente l nea. El car acter tabulador mueve el cursor hacia la derecha hasta que alcanza un punto de parada (cada cierto numero de espacios, que pueden variar de sistema a sistema). Los tabuladores son utiles para alinear columnas de texto, como la salida del anterior programa: 1.0 2.0 3.0 4.0 5.0 6.0 7.0 0.0 0.69314718056 1.09861228867 1.38629436112 1.60943791243 1.79175946923 1.94591014906
73
Si estos valores parecen extranos, recuerde que la funcion log usa la base e. Ya que las potencias de dos son importantes en la ciencias de la computacion, a menudo deseamos calcular logaritmos en base 2. Para este n podemos usar la siguiente formula: log2 x = Cambiando la salida del ciclo a: print x, \t, resulta en: 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 0.0 1.0 1.58496250072 2.0 2.32192809489 2.58496250072 2.80735492206 3.0 3.16992500144 math.log(x)/math.log(2.0) loge x loge 2 (7.1)
Podemos ver que 1, 2, 4, y 8 son potencias de dos porque sus logaritmos en base 2 son numeros enteros. Si deseamos calcular el logaritmo de m as potencias de dos podemos modicar el programa as : x = 1.0 while x < 100.0: print x, \t, math.log(x)/math.log(2.0) x = x * 2.0 Ahora, en lugar de agregar algo a x en cada iteracion del ciclo, produciendo una serie aritm etica, multiplicamos a x por algo constante, produciendo una serie geom etrica. El resultado es: 1.0 2.0 4.0 8.0 16.0 32.0 64.0 0.0 1.0 2.0 3.0 4.0 5.0 6.0
74
Iteracion
Gracias a los car acteres tabuladores entre las columnas, la posicion de la segunda columna no depende del numero de d gitos en la primera. Puede que las tablas de logaritmos no sirvan en nuestros d as, pero para los cient cos de la computacion saber las potencias de dos s es muy importante!. Como ejercicio, modique este programa de forma que despliegue las potencias de 2 hasta 65,536 (esto es 216 ). Imprima el resultado y familiaricese con las potencias de dos. El car acter diagonal invertido (backslash) \ indica el comienzo de una secuencia de escape. Estas secuencias se utilizan para representar car acteres invisibles como tabuladores y nuevas l neas. La secuencia \n representa una nueva l nea. Una secuencia de escape puede empezar en cualquier posicion de una cadena; en el ejemplo anterior, la secuencia de escape tabuladora es toda la cadena. Como cree que se representa un diagonal invertido en una cadena? Como ejercicio, escriba una sola cadena que produzca esta salida.
La primera l nea inicializa una variable llamada i, que actua como un contador o variable de ciclo. A medida que se ejecuta el ciclo, el valor de i se incrementa de 1 a 6. Cuando i es 7, el ciclo termina. En cada iteracion del ciclo despliega el valor 2*i, seguido de tres espacios.
75
De nuevo, la coma en la sentencia print suprime la nueva l nea. Despu es de que el ciclo termina la segunda sentencia print comienza una nueva l nea. La salida del programa es: 2 4 6 8 10 12
Ahora, usted probablemente imagine como imprimir una tabla de multiplicacion llamando a imprimaMultiplos repetidamente con diferentes argumentos. De hecho, podemos usar otro ciclo:
Iteracion
Observe lo similar que es este ciclo al ciclo interno de la funcion imprimaMultiplos. Todo lo que hicimos fue reemplazar la sentencia print con un llamado de funcion. La salida de este programa es una tabla de multiplicacion: 1 2 3 4 5 6 2 4 6 8 10 12 3 6 9 12 15 18 4 8 12 16 20 24 5 10 15 20 25 30 6 12 18 24 30 36
7.6. M as encapsulamiento
Para demostrar el encapsulamiento otra vez, tomemos el codigo de la seccion 7.5 y envolv amoslo en una funcion: def imprimirTablaMultiplicacion(): i = 1 while i <= 6: imprimaMultiplos(i) i = i + 1 Este proceso es un plan de desarrollo comun. Desarrollamos codigo escribiendo l neas afuera de cualquier funcion o digit andolas en el int erprete. Cuando funcionan, las ponemos dentro de una funcion. Este plan de desarrollo es particularmente util si usted no sabe, cuando est a empezando a programar, como dividir el programa en funciones. Este enfoque permite disenar a medida que se escribe codigo.
77
Las variables creadas dentro de una denicion de funcion son locales; no se puede acceder a ellas fuera de su funcion casa. Esto quiere decir que usted tiene la libertad de tener multiples variables con el mismo nombre en tanto no pertenezcan a la misma funcion. El diagrama de pila para este programa muestra que las dos variables llamadas i no son la misma. Se pueden referir a valores diferentes, y cambiar una de ellas no altera la otra.
imprimaTablaMultiplicacion i 1 2 3
imprimaMultiplos n 3 i
1 2
El valor de i en imprimaTabla va de 1 a 6. En el diagrama va por el valor 3. En la proxima iteracion del ciclo ser a 4. En cada iteracion, imprimaTabla llama a imprimaMultiplos con el valor actual de i como argumento. Ese valor se le asigna al par ametro n. Adentro de imprimaMultiplos el valor de i va de 1 a 6. En el diagrama, es 2. Cambiar esta variable no tiene efecto en el valor de i en imprimaTabla. Es legal y muy comun tener diferentes variables locales con el mismo nombre. Particularmente, nombres como i y j se usan frecuentemente como variables de ciclo. Si usted evita usarlas en una funcion porque ya las uso en otra, probablemente dicultar a la lectura del programa.
78 1 2 3 4 5 6 7 2 4 6 8 10 12 14 3 6 9 12 15 18 21 4 8 12 16 20 24 28 5 10 15 20 25 30 35 6 12 18 24 30 36 42
Iteracion
Esto est a bien, pero quiz as deseamos que la tabla sea cuadradacon el mismo numero de las y columnas. Para lograrlo, anadimos un par ametro a imprimaMultiplos que especique cu antas columnas debe tener la tabla. Solo por confundir, vamos a nombrarlo tope, demostrando que diferentes funciones pueden tener par ametros con el mismo nombre (igual que las variables locales). Aqu est a el programa completo: def imprimaMultiplos(n, tope): i = 1 while i <= tope: print n*i, \t, i = i + 1 print def imprimaTabla(tope): i = 1 while i <= tope: imprimaMultiplos(i, tope) i = i + 1 Note que cuando agregamos el nuevo par ametro tuvimos que cambiar la primera l nea de la funcion (la cabecera), y tambi en el lugar donde la funcion se llama en imprimaTabla. Como se esperaba, este programa genera una tabla cuadrada de siete-por-siete: 1 2 3 4 5 6 7 2 4 6 8 10 12 14 3 6 9 12 15 18 21 4 8 12 16 20 24 28 5 10 15 20 25 30 35 6 12 18 24 30 36 42 7 14 21 28 35 42 49
Cuando se generaliza una funcion adecuadamente, a menudo se obtiene un programa con capacidades que no se hab an planeado. Por ejemplo, podr amos apro-
7.9 Funciones
79
vechar el hecho de que ab = ba, que causa que todas las entradas de la tabla aparezcan dos veces para ahorrar tinta imprimiendo solamente la mitad de la tabla. Para lograrlo solo se necesita cambiar una l nea de imprimaTabla. Cambiamos: imprimaMultiplos(i, tope) por imprimaMultiplos(i, i) y se obtiene: 1 2 3 4 5 6 7
4 6 8 10 12 14
9 12 15 18 21
16 20 24 28
25 30 35
36 42
49
Como ejercicio, siga la ejecuci on de esta versi on de imprimaTabla y descifre c omo trabaja.
7.9. Funciones
Ya hemos mencionado los benecios de las funciones. Usted se estar a preguntado cuales son estos benecios. Aqu hay algunos: Nombrar una secuencia de sentencias aumenta la legibilidad de los programas y los hace m as f aciles de depurar. Dividir un programa grande en funciones permite separar partes de e ste, depurarlas aisladamente, y luego componerlas en un todo coherente. Las funciones facilitan la recursion y la iteracion. Las funciones bien disenadas resultan ser utiles para muchos programas. Una vez que usted escribe y depura una, se puede reutilizar.
80
Iteracion
7.10. Glosario
Asignacion multiple: realizar m as de una asignacion a una misma variable durante la ejecucion de un programa. Iteracion: la ejecucion repetida de un grupo de sentencias, ya sea en una funcion recursiva o en un ciclo. Ciclo: una sentencia o grupo de sentencias que se ejecuta repetidamente hasta que una condicion de terminacion se cumple. Ciclo innito: ciclo en el que la condicion de terminacion nunca se cumple. Cuerpo: las sentencias adentro de un ciclo. Variable de ciclo: variable que se usa como parte de la condicion de terminacion de un ciclo. Tab: (tabulador) car acter especial que causa el movimiento del cursor al siguiente punto de parada en la l nea actual. Nueva l nea: car acter que causa que el cursor se mueva al principio de la siguiente l nea. Cursor: marcador invisible que lleva pista de donde se va a imprimir el siguiente car acter. Secuencia de escape: car acter de escape (\) seguido por uno o m as car acteres imprimibles que se usan para denotar un car acter no imprimible. Encapsular: dividir un programa grande y complejo en componentes (como funciones) y aislarlos entre s (usando variables locales, por ejemplo). Generalizar: reemplazar algo innecesariamente espec co (como un valor constante) con algo general m as apropiado (como una variable o par ametro). La generalizacion aumenta la versatilidad del codigo, lo hace m as reutilizable y en algunos casos facilita su escritura. Plan de desarrollo: proceso para desarrollar un programa. En este cap tulo demostramos un estilo de desarrollo basado en escribir codigo que realiza compu tos simples y espec cos, para luego encapsularlo y generalizarlo.
Cap tulo 8
Cadenas
8.1. Un tipo de dato compuesto
Hasta aqu hemos visto tres tipos de datos: int, float y string. Las cadenas son cualitativamente diferentes de los otros dos tipos porque est an compuestas de piezas m as pequenaslos car acteres. Los tipos que comprenden piezas m as pequenas se denominan tipos de datos compuestos. Dependiendo de lo que hagamos podemos tratar un tipo compuesto como unidad o podemos acceder a sus partes. Esta ambiguedad es provechosa. El operador corchete selecciona un car acter de una cadena. >>> fruta = "banano" >>> letra = fruta[1] >>> print letra La expresion fruta[1] selecciona el car acter numero 1 de fruta. La variable letra se reere al resultado. Cuando desplegamos letra, obtenemos una pequena sorpresa: a La primera letra de "banano" no es a. A menos que usted sea un cient co de la computacion!.Por razones perversas, los cient cos de la computacion empiezan a contar desde cero. La letra numero 0 de "banano" es b. La letra 1 es a, y la letra 2 es n. Si usted desea la primera letra de una cadena se pone 0, o cualquier expresion con el valor 0, dentro de los corchetes:
Cadenas
La expresion en corchetes se denomina ndice. Un ndice especica un miembro de un conjunto ordenado, en este caso el conjunto de car acteres de la cadena. El ndice indica cual elemento desea usted, por eso se llama as . Puede ser cualquier expresion entera.
8.2. Longitud
La funcion len retorna el numero de car acteres en una cadena: >>> fruta = "banano" >>> len(fruta) 6 Para acceder a la ultima letra de una cadena usted podr a caer en algo como esto: longitud = len(fruta) ultima = fruta[longitud]
# ERROR!
Y no funcionar a. Causa un error en tiempo de ejecucion, IndexError: string index out of range. La razon yace en que no hay una letra 6 en "banana". Como empezamos a contar desde cero, las seis letras se numeran de 0 a 5. En general, para obtener la ultima letra, tenemos que restar 1 a la longitud: longitud = len(fruta) ultima = fruta[longitud-1] Alternativamente, podemos usar ndices negativos, que cuentan hacia atr as desde el n de la cadena. La expresion fruta[-1] retorna la ultima letra fruit[-2] retorna la penultima, y as sucesivamente.
8.3 Recorridos en cadenas y el ciclo for indice = 0 while indice < len(fruta): letra = fruta[indice] print letra indice = indice + 1
83
Este ciclo recorre la cadena y despliega cada letra en una l nea independiente. La condicion del ciclo es indice <len(fruta), as que cuando indice se hace igual a la longitud de la cadena, la condicion es falsa, y el cuerpo del ciclo no se ejecuta. El ultimo car acter accedido es el que tiene el ndice len(fruta)-1, es decir, el ultimo. Como ejercicio, escriba una funci on que tome una cadena como argumento y despliegue las letras al rev es, una por cada l nea. Usar un ndice para recorrer un conjunto de valores es tan comun que Python tiene una sintaxis alternativa m as simpleel ciclo for : for car acter in fruta: print car acter Cada vez que el ciclo itera, el proximo car acter en la cadena se asigna a la variable car acter. El ciclo continua hasta que no quedan m as car acteres. El siguiente ejemplo muestra como usar la concatenacion y un ciclo for para generar una serie en orden lexicogr aco. Lexicogr aco se reere a una lista en la que los elementos aparecen en orden alfab etico. Por ejemplo, en el libro Make Way for Ducklings de Robert McCloskey, los nombres de los patos son Jack, Kack, Lack, Mack, Nack, Ouack, Pack, and Quack. Este ciclo los despliega en orden: prefijos = "JKLMNOPQ" sufijo = "ack" for letra in prefijos: print letra + sufijo La salida de este programa es: Jack Kack Lack Mack Nack Oack Pack Qack
84
Cadenas
Por supuesto que hay un error, ya que Ouack y Quack no est an bien deletreados. Como ejercicio, modique el programa para corregir este error.
"banano"
indice 0 1 2 3 4 5 6 Si usted omite el primer ndice (despues de los puntos seguidos), el segmento comienza en el inicio de la cadena. Si se omite el segundo ndice, el segmento va hasta el nal. Entonces:
>>> fruta = "banana" >>> fruta[:3] ban >>> fruta[3:] ano Que cree que signica s[:]?
8.6 Las cadenas son inmutables if palabra == "banana": print "No hay bananas!"
85
Las otras operaciones de comparacion son utiles para poner las palabras en orden alfab etico: if palabra < "banana": print "Su palabra," + palabra + ", va antes que banana." elif palabra > "banana": es de banana." print "Su palabra," + palabra + ", va despu else: print "No hay bananas!" Sin embargo, usted debe ser consciente de que Python no maneja las letras minuscu las y mayusculas de la misma forma en que lo hace la gente. Todas las letras mayusculas vienen antes de las minusculas. Si palabra vale Zebrala salida ser a: Su palabra, Zebra, va antes que banana. Este problema se resuelve usualmente convirtiendo las cadenas a un formato comun, todas en minusculas por ejemplo, antes de hacer la comparacion. Un problema m as dif cil es lograr que el programa reconozca que una zebra no es una fruta.
En lugar de desplegar Jola mundo!, se produce un error en tiempo de ejecucion TypeError: object doesnt support item assignment. Las cadenas son inmutables, lo que quiere decir que no se puede cambiar una cadena existente. Lo m aximo que se puede hacer es crear otra cadena que cambia un poco a la original: saludo = "Hola mundo!" nuevoSaludo = J + saludo[1:] print nuevoSaludo La solucion consiste en concatenar la primera nueva letra con un segmento de saludo. Esto no tiene efecto sobre la primera cadena, usted puede chequearlo.
86
Cadenas
8.9 El modulo string Como ejercicio, encapsule este c odigo en una funci on llamada contarLetras, y general cela de forma que reciba la cadena y la letra como par ametros. Otro ejercicio, reescriba esta funci on de forma que en lugar de recorrer la cadena, llame a la funci on buscar anterior que recibe tres par ametros.
87
88
Cadenas
8.11 Glosario
89
Un car acter de los que pertenecen a Whitespace mueve el cursor sin imprimir nada. Crean un espacio en blanco que se puede evidenciar entre car acteres. La constante string.whitespace contiene todos los car acteres que representan espacios en blanco: espacio, tab (\t), y nueva l nea (\n). Hay otras funciones utiles en el modulo string, pero este libro no es un manual de referencia. Para esto usted puede consultar la referencia de las bibliotecas de Python (Python Library Reference). Adem as, hay un gran cumulo de documentacion en el sitio web de Python www.python.org.
8.11. Glosario
Tipo de dato compuesto: un tipo de dato en el que los valores est an compuestos por componentes o elementos, que, a su vez, son valores. Recorrido: iteracion sobre todos los elementos de un conjunto ejecutando una operacion similar en cada uno. Indice: variable o valor que se usa para seleccionar un miembro de un conjunto ordenado, tal como los car acteres de una cadena. Tambi en se puede usar el t ermino posici on como sinonimo de ndice. Segmento: parte de una cadena, especicada por un rango de ndices. Mutable: un tipo de dato compuesto a cuyos elementos pueden asignarseles nuevos valores. Contador: una variable que se usa para contar algo, usualmente se inicializa en cero y se incrementa posteriormente dentro de un ciclo. Incrementar: agregar uno al valor de una variable Decrementar: restar uno al valor de una variable Espacio en blanco: cualquiera de los car acteres que mueven el cursor sin imprimir nada visible. La constante string.whitespace contiene todos los car acteres que representan espacios en blanco.
Cap tulo 9
Listas
Una lista es un conjunto ordenado de valores que se identican por medio de un ndice. Los valores que componen una lista se denominan elementos. Las listas son similares a las cadenas, que son conjuntos ordenados de car acteres, pero son mas generales, ya que pueden tener elementos de cualquier tipo de dato. Las listas y las cadenasy otras conjuntos ordenados que veremos se denominan secuencias.
92
Listas
La funcion range toma dos argumentos y retorna una lista que contiene todos los enteros desde el primero hasta el segundo, incluyendo el primero y no el ultimo! Hay otras formas de usar a range. Con un solo argumento crea una lista que empieza en 0: >>> range(10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Si hay un tercer argumento, este especica el espacio entre los valores sucesivos, que se denomina el tamano del paso. Este ejemplo cuenta de 1 a 10 con un paso de tamano 2: >>> range(1, 10, 2) [1, 3, 5, 7, 9] Finalmente, existe una lista especial que no contiene elementos. Se denomina lista vac a, y se denota con []. Con todas estas formas de crear listas ser a decepcionante si no pudi eramos asignar listas a variables o pasarlas como par ametros a funciones. De hecho, podemos hacerlo: >>> vocabulario = ["mejorar", "castigar", "derrocar"] >>> numeros = [17, 123] >>> vacia = [] >>> print vocabulario, numeros, vacia ["mejorar", "castigar", "derrocar"] [17, 123] []
9.3 Longitud de una lista >>> numeros[3-2] 5 >>> numeros[1.0] TypeError: sequence index must be integer
93
Si usted intenta leer o escribir un elemento que no existe, obtiene un error en tiempo de ejecucion: >>> numeros[2] = 5 IndexError: list assignment index out of range Si el ndice tiene un valor negativo, cuenta hacia atr as desde el nal de la lista: >>> numeros[-1] 5 >>> numeros[-2] 17 >>> numeros[-3] IndexError: list index out of range numeros[-1] es el ultimo elemento de la lista, numeros[-2] es el penultimo, y numeros[-3] no existe. Usualmente se usan variables de ciclo como ndices de listas: combatientes = ["guerra", "hambruna", "peste", "muerte"] i = 0 while i < 4: print combatientes[i] i = i + 1 Este ciclo while cuenta de 0 a 4. Cuando la variable de ciclo i es 4, la condicion falla y el ciclo termina. El cuerpo del ciclo se ejecuta solamente cuando i es 0, 1, 2, y 3. En cada iteracion del ciclo, la variable i se usa como un ndice a la lista, imprimiendo el i- esimo elemento. Este patron se denomina recorrido de una lista.
94
Listas
combatientes = ["guerra", "hambruna", "peste", "muerte"] i = 0 while i < len(combatientes): print combatientes[i] i = i + 1 La ultima vez que el ciclo se ejecuta i es len(combatientes) - 1, que es la posicion del ultimo elemento. Cuando i es igual a len(combatientes), la condicion falla y el cuerpo no se ejecuta, lo que est a muy bien , ya que len(combatientes) no es un ndice v alido. Aunque una lista puede contener a otra, la lista anidada se sigue viendo como un elemento unico. La longitud de esta lista es cuatro: [basura!, 1, [Brie, Roquefort, Pol le Veq], [1, 2, 3]]
Como ejercicio, escriba un ciclo que recorra la lista anterior imprimiendo la longitud de cada elemento. Qu e pasa si usted le pasa un entero a len?
9.4. Pertenencia
in es un operador booleano que chequea la pertenencia de un valor a una secuencia. Lo usamos en la Seccion 8.10 con cadenas, pero tambi en funciona con listas y otras secuencias: >>> combatientes = ["guerra", "hambruna", "peste", "muerte"] >>> peste in combatientes True >>> corrupcion in combatientes False Ya que peste es un miembro de la lista combatientes, el operador in retorna cierto. Como corrupcion no est a en la lista, in retorna falso. Podemos usar el operador logico not en combinacion con el in para chequear si un elemento no es miembro de una lista: >>> corrupcion not in combatientes True
95
96
Listas
Similarmente, el operador * repite una lista un numero de veces determinado: >>> [0, >>> [1, [0] * 4 0, 0, 0] [1, 2, 3] * 3 2, 3, 1, 2, 3, 1, 2, 3]
El primer ejemplo repite [0] cuatro veces. El segundo repite [1, 2, 3] tres veces.
9.9 Otras operaciones sobre listas Tambi en podemos eliminar varios elementos asign andoles la lista vac a: >>> lista = [a, b, c, d, e, f] >>> lista[1:3] = [] >>> print lista [a, d, e, f]
97
Igualmente, podemos agregar elementos a una lista apret andolos dentro de un segmento vac o en la posicion que deseamos: >>> lista = [a, d, f] >>> lista[1:1] = [b, c] >>> print lista [a, b, c, d, f] >>> lista[4:4] = [e] >>> print lista [a, b, c, d, e, f]
Listas
En un caso, a y b se reeren a cosas distintas que tienen el mismo valor. En el segundo caso, se reeren a la misma cosa. Estas cosas tienen nombresse denominan objetos. Un objeto es algo a lo que se puede referir una variable. Cada objeto tiene un identicador unico, que podemos obtener con la funcion id. Imprimiendo el identicador de a y b, podemos saber si se reeren al mismo objeto. >>> id(a) 135044008 >>> id(b) 135044008 De hecho, obtenemos el mismo identicador dos veces, lo que nos dice que Python solo creo una cadena, y que a y b se reeren a ella. Las listas, por otro lado, se comportan de manera diferente. Cuando creamos dos listas obtenemos dos objetos: >>> a = [1, 2, 3] >>> b = [1, 2, 3] >>> id(a) 135045528 >>> id(b) 135041704
99
9.11. Alias
Como las variables se pueden referir a objetos, si asignamos una variable a otra, las dos se referir an al mismo objeto: >>> a = [1, 2, 3] >>> b = a En este caso el diagrama de estados luce as :
a b [ 1, 2, 3 ]
Como la misma lista tiene dos nombres distintos, a y b, podemos decir que b es un alias de a. Los cambios que se hagan a trav es de un alias afectan al otro: >>> b[0] = 5 >>> print a [5, 2, 3] Aunque este comportamiento puede ser util, algunas veces puede ser indeseable. En general, es m as seguro evitar los alias cuando se est a trabajando con objetos mutables. Para objetos inmutables no hay problema. Esta es la razon por la que Python tiene la libertad de crear alias a cadenas cuando ve la oportunidad de economizar memoria. Pero tenga en cuenta que esto puede variar en las diferentes versiones de Python; por lo tanto no es recomendable realizar programas que dependan de este comportamiento.
Listas
Al tomar cualquier segmento de a creamos una nueva lista. En este caso el segmento comprende toda la lista. Ahora podemos realizar cambios a b sin preocuparnos por a: >>> b[0] = 5 >>> print a [1, 2, 3] Como ejercicio, dibuje un diagrama de estados para a y b antes y despu es de este cambio.
Como el objeto lista est a compartido por dos marcos, lo dibujamos en el medio. Si una funcion modica un par ametro de tipo lista, el que hizo el llamado ve los cambios. Por ejemplo, borrarCabeza borra el primer elemento de una lista:
9.14 Listas anidadas def borrarCabeza(lista): del lista[0] Y se puede usar as :: >>> >>> >>> [2, numeros = [1, 2, 3] borrarCabeza(numeros) print numeros 3]
101
Si una funcion retorna una lista, retorna una referencia a ella. Por ejemplo, la funcion cola retorna una lista que contiene todos los elementos, excepto el primero: def cola(lista): return lista[1:] cola se puede usar as : >>> >>> >>> [2, numeros = [1, 2, 3] resto = cola(numeros) print resto 3]
Como el valor de retorno se creo con el operador segmento, es una nueva lista. La creacion de resto, y los cambios subsecuentes sobre esta variable no tienen efecto sobre numeros.
102
Listas
9.15. Matrices
Las listas anidadas se usan a menudo matriz: 1 4 7 se puede representar as : para representar matrices. Por ejemplo, la
2 3 5 6 8 9
>>> matriz = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] matriz es una lista con tres elementos, cada uno es una la. Podemos seleccionar una la de la manera usual: >>> matriz[1] [4, 5, 6] O podemos extraer un elemento individual de la matriz usando dos ndices: >>> matriz[1][1] 5 El primero escoge la la, y el segundo selecciona la columna. Aunque esta forma de representar matrices es comun, no es la unica posibilidad. Una pequena variacion consiste en usar una lista de columnas en lugar de una lista de las. M as adelante veremos una alternativa m as radical, usando un diccionario.
9.17 Glosario
103
Note que el delimitador no aparece en la lista resultante. La funcion join es la inversa de split. Toma una lista de cadenas y las concatena con un espacio entre cada par: >>> m = [La, vida, es, un, ratico] >>> string.join(m) La vida es un ratico Como split, join puede recibir un argumento adicional separador que se inserta entre los elementos: >>> string.join(m, _) La_vida_es_un_ratico Como ejercicio, describa la relaci on entre las expresiones cadena y string.join(string.split(cadena)). Son iguales para todas las cadenas? Cuando ser an diferentes?
9.17. Glosario
Lista: coleccion de objetos que recibe un nombre. Cada objeto se identica con un ndice o numero entero positivo. Indice: valor o variable entero que indica la posicion de un elemento en una lista. Elemento: uno de los valores dentro de una lista (u otra secuencia). El operador corchete selecciona elementos de una lista. Secuencia: los tipos de datos que contienen un conjunto ordenado de elementos, identicados por ndices. Lista anidada: lista que es elemento de otra lista. Recorrido de una lista: es el acceso secuencial de cada elemento de una lista. Objeto: una cosa a la que una variable se puede referir. Alias: cuando varias variables tienen referencias hacia el mismo objeto. Clonar: crear un objeto con el mismo valor que un objeto preexistente. Copiar una referencia a un objeto crea un alias, pero no clona el objeto. Delimitador: car acter o cadena que se usa para indicar el lugar donde una cadena debe ser separada.
Cap tulo 10
Tuplas
10.1. Mutabilidad y tuplas
Hasta aqu , usted ha visto dos tipos de datos compuestos: cadenas, que est an compuestas de car acteres; y listas, compuestas de elementos de cualquier tipo. Una de las diferencias que notamos es que los elementos de una lista pueden modicarse, pero los car acteres en una cadena no. En otras palabras, las cadenas son inmutables y las listas son mutables. Hay otro tipo de dato en Python denominado tupla, que se parece a una lista, con la excepcion de que es inmutable. Sint acticamente, una tupla es una lista de valores separados por comas: >>> tupla = a, b, c, d, e Aunque no es necesario, se pueden encerrar entre par entesis: >>> tupla = (a, b, c, d, e) Para crear una tupla con un unico elemento, tenemos que incluir la coma nal: >>> t1 = (a,) >>> type(t1) <type tuple> Sin la coma, Python creer a que (a) es una cadena en par entesis: >>> t2 = (a) >>> type(t2) <type string>
106
Tuplas
Las operaciones sobre tuplas son las mismas que vimos con las listas. El operador corchete selecciona un elemento de la tupla. >>> tupla = (a, b, c, d, e) >>> tupla[0] a Y el operador segmento selecciona un rango de elementos: >>> tupla[1:3] (b, c) Pero si intentamos modicar un elemento de la tupla obtenemos un error: >>> tupla[0] = A TypeError: object doesnt support item assignment Aunque no podemos modicar los elementos, s podemos modicar toda la tupla: >>> tupla = (A,) + tupla[1:] >>> tupla (A, b, c, d, e) >>> tupla = (1,2,3) >>> tupla
107
El lado izquierdo es una tupla de variables; el derecho es una tupla de valores. Cada valor se asigna a su respectiva variable en el orden en que se presenta. Las expresiones en el lado derecho se evaluan antes de cualquier asignacion. Esto hace a la asignacion de tuplas una herramienta bastante vers atil. Naturalmente, el numero de variables a la izquierda y el numero de valores a la derecha deben coincidir. >>> a, b, c, d = 1, 2, 3 ValueError: unpack tuple of wrong size
108
Tuplas
10.6 Conteo
109
La probaremos con ocho elementos. Para depurar es una buena idea empezar con pocos datos: >>> listaAleatoria(8) 0.15156642489 0.498048560109 0.810894847068 0.360371157682 0.275119183077 0.328578797631 0.759199803101 0.800367163582 Los numeros generados por random deben distribuirse uniformemente, lo que signica que cada valor es igualmente probable. Si dividimos el rango de valores posibles en regiones del mismo tamano y contamos el numero de veces que un valor aleatorio cae en cada region, deber amos obtener un resultado aproximado en todas las regiones. Podemos probar esta hipotesis escribiendo un programa que divida el rango en regiones y cuente el numero de valores que caen en cada una.
10.6. Conteo
Un enfoque que funciona en problemas como e ste es dividir el problema en subproblemas que se puedan resolver con un patron computacional que ya sepamos. En este caso, necesitamos recorrer una lista de numeros y contar el numero de veces que un valor cae en un rango dado. Esto parece familiar. En la Seccion 8.8, escribimos un programa que recorr a una cadena y contaba el numeros de veces que aparec a una letra determinada. Entonces podemos copiar el programa viajo para adaptarlo posteriormente a nuestro problema actual. El original es: cont = 0 for c in fruta: if c == a: cont = cont + 1 print cont El primer paso es reemplazar fruta con lista y c por num. Esto no cambia el programa, solo lo hace m as legible.
110
Tuplas
El segundo paso es cambiar la prueba. No queremos buscar letras. Queremos ver si num est a entre dos valores dados inf y sup. cont = 0 for num in lista if inf < num < sup: cont = cont + 1 print cont El ultimo paso consiste en encapsular este codigo en una funcion denominada enRegion. Los par ametros son la lista y los valores inf y sup. def enRegion(lista, inf, sup): cont = 0 for num in lista: if inf < num < sup: cont = cont + 1 return cont Copiando y modicando un programa existente fuimos capaces de escribir esta funcion r apidamente y ahorrarnos un buen tiempo de depuracion. Este plan de desarrollo se denomina concordancia de patrones. Si se encuentra trabajando en un problema que ya ha resuelto antes, reutilice la solucion.
Hay dos problemas. Uno es que siempre tenemos que crear nuevos nombres de variables para cada resultado. El otro es que tenemos que calcular el rango de cada region.
111
Primero resolveremos el segundo problema. Si el numero de regiones est a dado por la variable numRegiones, entonces, el ancho de cada region est a dado por la expresion 1.0/numRegiones. Usaremos un ciclo para calcular el rango de cada region. La variable de ciclo i cuenta de 1 a numRegiones-1: ancho = 1.0 / numRegiones for i in range(numRegiones): inf = i * ancho sup = inf + ancho print inf, " hasta ", sup Para calcular el extremo inferior de cada region, multiplicamos la variable de ciclo por el ancho. El extremo superior est a a un ancho de region de distancia. Con numRegiones = 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 Usted puede conrmar que cada region tiene el mismo ancho, que no se solapan y que cubren el rango completo de 0.0 a 1.0. Ahora regresemos al primer problema. Necesitamos una manera de almacenar ocho enteros, usando una variable para indicarlos uno a uno. Usted debe estar pensando una lista! Tenemos que crear la lista de regiones fuera del ciclo, porque esto solo debe ocurrir una vez. Dentro del ciclo, llamaremos a enRegion repetidamente y actualizaremos el ie simo elemento de la lista: numRegiones = 8 Regiones = [0] * numRegiones ancho = 1.0 / numRegiones for i in range(numRegiones): inf = i * ancho sup = inf + ancho Regiones[i] = enRegion(lista, inf, sup) print Regiones Con una lista de 1000 valores, este codigo produce la siguiente lista de conteo:
Tuplas
Todos estos valores est an muy cerca a 125, que es lo que esperamos. Al menos, est an lo sucientemente cerca como para creer que el generador de numeros pseudoaleatorios est a funcionando bien. Como ejercicio, envuelva este c odigo en una funci on, pru ebela con listas m as grandes y vea si los numeros de valores en cada regi on tienden a emparejarse
10.9 Glosario
113
10.9. Glosario
Tipo inmutable: es un tipo de dato en el que los elementos no pueden ser modicados. Las asignaciones a elementos o segmentos de tipos inmutables causan errores. Las cadenas y las tuplas son inmutables. Tipo mutable: tipo de dato en el que los elementos pueden ser modicados. Todos los tipos mutables son compuestos. Las listas y los diccionarios son mutables. Tupla: tipo de dato secuencial similar a la lista, pero inmutable. Las tuplas se pueden usar donde se requiera un tipo inmutable, por ejemplo como llaves de un diccionario. Asignacion de tuplas: una asignacion a todos los elementos de una tupla en una sola sentencia. La asignacion ocurre en paralelo y no secuencialmente. Es util para intercambiar valores de variables. Determin stico: programa que hace lo mismo cada vez que se llama. Pseudoaleatoria: secuencia de numeros que parece aleatoria, pero en realidad es el resultado de un computo determin stico, bien disenado. Histograma: lista de enteros en la que cada elemento cuenta el numero de veces que algo sucede. Correspondencia de patrones: plan de desarrollo de programas que implica identicar un patron computacional familiar y copiar la solucion de un problema similar.
Cap tulo 11
Diccionarios
Los tipos compuestos que ha visto hasta ahora (cadenas, listas y tuplas) usan enteros como ndices. Si usted intenta usar cualquier otro tipo como ndice provocar a un error. Los diccionarios son similares a otros tipos compuestos, excepto en que pueden usar como ndice cualquier tipo inmutable. A modo de ejemplo, crearemos un diccionario que traduzca palabras inglesas al espanol. En este diccionario, los ndices son cadenas (strings). Una forma de crear un diccionario es empezar con el diccionario vac o y anadir elementos. El diccionario vac o se expresa como {}: >>> ing_a_esp = {} >>> ing_a_esp[one] = uno >>> ing_a_esp[two] = dos La primera asignacion crea un diccionario llamado ing a esp; las otras asignaciones anaden nuevos elementos al diccionario. Podemos desplegar el valor actual del diccionario del modo habitual: >>> print ing_a_esp {one: uno, two: dos} Los elementos de un diccionario aparecen en una lista separada por comas. Cada entrada contiene un ndice y un valor separado por dos puntos (:). En un diccionario, los ndices se llaman claves, por eso los elementos se llaman pares clave-valor. Otra forma de crear un diccionario es dando una lista de pares clave-valor con la misma sintaxis que la salida del ejemplo anterior: >>> ing_a_esp={one: uno, two: dos, three: tres}
116
Diccionarios
Si volvemos a imprimir el valor de ing a esp, nos llevamos una sorpresa: >>> print ing_a_esp {one: uno, three: tres, two: dos} Los pares clave-valor no est an en orden! Afortunadamente, no necesitamos preocuparnos por el orden, ya que los elementos de un diccionario nunca se indexan con ndices enteros. En lugar de eso, usamos las claves para buscar los valores correspondientes: >>> print ing_a_esp[two] dos La clave two nos da el valor dos aunque aparezca en el tercer par clave-valor.
117
118
Diccionarios
119
Una posible alternativa consiste en usar un diccionario. Como claves, podemos usar tuplas que contengan los numeros de la y columna. Esta es la representacion de la misma matriz por medio de un diccionario: >>> matriz = {(0,3): 1, (2, 1): 2, (4, 3): 3} Solo hay tres pares clave-valor, uno para cada elemento de la matriz diferente de cero. Cada clave es una tupla, y cada valor es un entero. Para acceder a un elemento de la matriz, podemos usar el operador []: >>> matriz[0,3] 1 Observe que la sintaxis para la representacion por medio del diccionario no es la misma de la representacion por medio de la lista anidada. En lugar de dos ndices enteros, usamos un ndice compuesto que es una tupla de enteros. Hay un problema. Si apuntamos a un elemento que es cero, se produce un error porque en el diccionario no hay una entrada con esa clave: >>> matriz[1,3] KeyError: (1, 3) El m etodo get soluciona este problema: >>> matriz.get((0,3), 0) 1 El primer argumento es la clave; el segundo argumento es el valor que debe devolver get en caso de que la clave no est e en el diccionario: >>> matriz.get((1,3), 0) 0 get mejora sensiblemente la sem antica del acceso a una matriz dispersa. L astima que la sintaxis no sea tan clara!
120
Diccionarios
11.5. Pistas
Si ha jugado con la funcion fibonacci de la Seccion 6.7, es posible que haya notado que cuanto m as grande es el argumento que recibe, m as tiempo le cuesta ejecutarse. De hecho, el tiempo de ejecucion aumenta muy r apidamente. En nuestra m aquina, fibonacci(20) acaba instant aneamente, fibonacci(30) tarda m as o menos un segundo, y fibonacci(40) tarda una eternidad. Para entender por qu e, observe este gr aco de llamadas de fibonacci con n=4:
fibonacci n 4
fibonacci n 3
fibonacci n 2
fibonacci n 2
fibonacci n 1
fibonacci n 1
fibonacci n 0
fibonacci n 1
fibonacci n 0
Un gr aco de llamadas muestra un conjunto de cajas de funcion con l neas que conectan cada caja con las cajas de las funciones a las que llama. En lo alto del gr aco, fibonacci con n=4 llama a fibonacci con n=3 y n=2. A su vez, fibonacci con n=3 llama a fibonacci con n=2 y n=1. Y as sucesivamente. Cuente cu antas veces se llama a fibonacci(0) y fibonacci(1). Esta funcion es una solucion ineciente para el problema, y empeora mucho a medida que crece el argumento. Una buena solucion es llevar un registro de los valores que ya se han calculado almacen andolos en un diccionario. A un valor que ya ha sido calculado y almacenado para un uso posterior se le llama pista. Aqu hay una implementacion de fibonacci con pistas: anteriores = {0:1, 1:1} def fibonacci(n): if anteriores.has_key(n):
11.6 Enteros largos return anteriores[n] else: nuevoValor = fibonacci(n-1) + fibonacci(n-2) anteriores[n] = nuevoValor return nuevoValor
121
El diccionario llamado anteriores mantiene un registro de los valores de Fibonacci que ya conocemos. El programa comienza con solo dos pares: 0 corresponde a 1 y 1 corresponde a 1. Siempre que se llama a fibonacci comprueba si el diccionario contiene el resultado ya calculado. Si est a ah , la funcion puede devolver el valor inmediatamente sin hacer m as llamadas recursivas. Si no, tiene que calcular el nuevo valor. El nuevo valor se anade al diccionario antes de que la funcion retorne. Con esta version de fibonacci, nuestra m aquina puede calcular fibonacci(40) en un abrir y cerrar de ojos. Pero cuando intentamos calcular fibonacci(50), nos encontramos con otro problema: >>> fibonacci(50) OverflowError: integer addition La respuesta, como se ver a en un momento, es 20.365.011.074. El problema es que este numero es demasiado grande para caber en un entero de Python. Se desborda. Afortunadamente, hay una solucion f acil para este problema.
122
Diccionarios
Todas las operaciones matem aticas funcionan sobre los datos de tipo long int, as que no tenemos que hacer mucho para adaptar fibonacci: >>> anterior = {0:1L, 1:1L} >>> fibonacci(50) 20365011074L Simplemente cambiando el contenido inicial de anteriores cambiamos el comportamiento de fibonacci. Los primeros dos numeros de la secuencia son de tipo long int, as que todos los numeros subsiguientes lo ser an tambi en. Como ejercicio, modica factorial de forma que produzca un long int como resultado.
11.8 Glosario
123
Usted ya ha visto el m etodo items aplicable a los diccionarios; sort es un m etodo aplicable a listas. Hay varios m as, como append, extend y reverse. Consulte la documentacion de Python para ver los detalles.
11.8. Glosario
Diccionario: es una coleccion de pares clave-valor que establece una correspondencia entre claves y valores. Las claves pueden ser de cualquier tipo inmutable, los valores pueden ser de cualquier tipo. Clave: un valor que se usa para buscar una entrada en un diccionario. Par clave-valor: uno de los elementos de un diccionario, tambi en llamado asociacion. M etodo: tipo de funcion al que se llama con una sintaxis diferente y al que se invoca sobre un objeto. Invocar: llamar a un m etodo. Pista: almacenamiento temporal de un valor precalculado, para evitar c alculos redundantes. Desbordamiento: resultado num erico que es demasiado grande para representarse en formato num erico.
Cap tulo 12
Archivos y excepciones
Cuando un programa se est a ejecutando, sus datos est an en la memoria. Cuando un programa termina, o se apaga el computador, los datos de la memoria desaparecen. Para almacenar los datos de forma permanente se deben poner en un archivo. Leyendo y escribiendo archivos, los programas pueden intercambiar informacion entre ellos y generar formatos imprimibles como PDF. Normalmente los archivos se guardan en un disco duro, disquete o CD-ROM. Cuando hay un gran numero de archivos, suelen estar organizados en directorios (tambi en llamados carpetas). Cada archivo se identica con un nombre unico, o una combinacion de nombre de archivo y nombre de directorio. Trabajar con archivos se parece mucho a hacerlo con libros. Para usar un libro, hay que abrirlo. Cuando uno ha terminado, hay que cerrarlo. Mientras el libro est a abierto, se puede escribir en e l o leer de e l. En cualquier caso, uno sabe en qu e lugar del libro se encuentra. Casi siempre se lee un libro segun su orden natural, pero tambi en se puede ir saltando de p agina en p agina. Todo esto sirve tambi en para los archivos. Para abrir un archivo, se especica su nombre y se indica si se desea leer o escribir. La apertura de un archivo crea un objeto archivo. En este ejemplo, la variable f apunta al nuevo objeto archivo. >>> f = open("test.dat","w") >>> print f <open file test.dat, mode w at fe820> La funcion open toma dos argumentos: el primero, es el nombre del archivo y el segundo, el modo. El modo "w" signica que lo estamos abriendo para escribir. Si no hay un archivo llamado test.dat se crear a. Si ya hay uno, el archivo que estamos escribiendo lo reemplazar a.
126
Archivos y excepciones
Al imprimir el objeto archivo, vemos el nombre del archivo, el modo y la localizacion del objeto. Para escribir datos en el archivo invocamos al m etodo write sobre el objeto archivo: >>> f.write("Ya es hora") >>> f.write("de cerrar el archivo") El cierre del archivo le dice al sistema que hemos terminado de escribir y deja el archivo listo para leer: >>> f.close() Ya podemos abrir el archivo de nuevo, esta vez para lectura, y poner su contenido en una cadena. Esta vez el argumento de modo es "r", para lectura: >>> f = open("test.dat","r") Si intentamos abrir un archivo que no existe, recibimos un mensaje de error: >>> f = open("test.cat","r") IOError: [Errno 2] No such file or directory: test.cat Como era de esperar, el m etodo read lee datos del archivo. Sin argumentos, lee el archivo completo: >>> texto = f.read() >>> print texto Ya es horade cerrar el archivo No hay un espacio entre hora y de porque no escribimos un espacio entre las cadenas. read tambi en puede aceptar un argumento que le indica cu antos car acteres leer: >>> f = open("test.dat","r") >>> print f.read(7) Ya es h Si no quedan sucientes car acteres en el archivo, read devuelve los que haya. Cuando llegamos al nal del archivo, read devuelve una cadena vac a: >>> print f.read(1000006) orade cerrar el archivo >>> print f.read() >>>
127
La siguiente funcion copia un archivo, leyendo y escribiendo los car acteres de cincuenta en cincuenta. El primer argumento es el nombre del archivo original; el segundo es el nombre del archivo nuevo: def copiaArchivo(archViejo, archNuevo): f1 = open(archViejo, "r") f2 = open(archNuevo, "w") while True: texto = f1.read(50) if texto == "": break f2.write(texto) f1.close() f2.close() return La sentencia break es nueva. Su ejecucion interrumpe el ciclo; el ujo de la ejecucion pasa a la primera sentencia despu es del while. En este ejemplo, el ciclo while es innito porque la condicion True siempre es verdadera. La unica forma de salir del ciclo es ejecutar break, lo que sucede cuando texto es una cadena vac a, y esto pasa cuando llegamos al nal del archivo.
128
Archivos y excepciones
readlines devuelve todas las l neas que queden como una lista de cadenas: >>> print f.readlines() [l nea dos\012, l nea tres\012] En este caso, la salida est a en forma de lista, lo que signica que las cadenas aparecen con comillas y el car acter de salto de l nea aparece como la secuencia de escape 012. Al nal del archivo, readline devuelve una cadena vac a y readlines devuelve una lista vac a: >>> print f.readline() >>> print f.readlines() [] Lo que sigue es un ejemplo de un programa de proceso de l neas. filtraArchivo hace una copia de archViejo, omitiendo las l neas que comienzan por #: def filtraArchivo(archViejo, archNuevo): f1 = open(archViejo, "r") f2 = open(archNuevo, "w") while 1: texto = f1.readline() if texto == "": break if texto[0] == #: continue f2.write(texto) f1.close() f2.close() return La sentencia continue termina la iteracion actual del ciclo, pero sigue haciendo las que le faltan. El ujo de ejecucion pasa al principio del ciclo, comprueba la condicion y continua normalmemente. As , si texto es una cadena vac a, el ciclo termina. Si el primer caracter de texto es una almohadilla (#), el ujo de ejecucion va al principio del ciclo. Solo si ambas condiciones fallan copiamos texto en el archivo nuevo.
129
Una alternativa es usar el operador de formato %. Cuando aplica a enteros, % es el operador de modulo. Pero cuando el primer operando es una cadena, % es el operador de formato. El primer operando es la cadena de formato, y el segundo, una tupla de expresiones. El resultado es una cadena que contiene los valores de las expresiones, formateados de acuerdo con la cadena de formato. A modo de ejemplo simple, la secuencia de formato "%d" signica que la primera expresion de la tupla deber a formatearse como un entero. Aqu la letra d quiere decir decimal: >>> motos = 52 >>> "%d" % motos 52 El resultado es la cadena 52, que no debe confundirse con el valor entero 52. Una secuencia de formato puede aparecer en cualquier lugar de la cadena de formato, de modo que podemos incrustar un valor en una frase: >>> motos = 52 >>> "En julio vendimos %d motos." % motos En julio vendimos 52 motos. La secuencia de formato "%f" formatea el siguiente elemento de la tupla como un numero en punto otante, y "%s" formatea el siguiente elemento como una cadena: as ganamos %f millones de %s." % (4,1.2,pesos) >>> "En %d d En 34 d as ganamos 1.200000 millones de pesos. Por defecto, el formato de punto otante imprime seis decimales. El numero de expresiones en la tupla tiene que coincidir con el numero de secuencias de formato de la cadena. Igualmente, los tipos de las expresiones deben coincidir con las secuencias de formato: >>> "%d %d TypeError: >>> "%d" % TypeError: %d" % (1,2) not enough arguments for format string d olares illegal argument type for built-in operation
En el primer ejemplo no hay sucientes expresiones; en el segundo, la expresion es de un tipo incorrecto. Para tener m as control sobre el formato de los numeros, podemos detallar el nume ro de d gitos como parte de la secuencia de formato:
Archivos y excepciones
El numero tras el signo de porcentaje es el numero m nimo de espacios que ocupar a el numero. Si el valor necesita menos d gitos, se anaden espacios en blanco delante del numero. Si el numero de espacios es negativo, se anaden los espacios tras el numero: >>> "%-6d" % 62 62 Tambi en podemos especicar el numero de decimales para los numeros en coma otante: >>> "%12.2f" % 6.1 6.10 En este ejemplo, el resultado ocupa doce espacios e incluye dos d gitos tras la coma. Este formato es util para imprimir cantidades de dinero con las comas alineadas. Imagine, por ejemplo, un diccionario que contiene los nombres de los estudiantes como clave y las tarifas horarias como valores. He aqu una funcion que imprime el contenido del diccionario como de un informe formateado: def informe (tarifas) : estudiantes = tarifas.keys() estudiantes.sort() for estudiante in estudiantes : print "%-20s %12.02f"%(estudiante, tarifas[estudiante]) Para probar la funcion, crearemos un pequeno diccionario e imprimiremos el contenido: >>> tarifas = {mar a: 6.23, jos e: 5.45, jes us: 4.25} >>> informe (tarifas) jos e 5.45 jes us 4.25 mar a 6.23 Controlando el ancho de cada valor nos aseguramos de que las columnas van a quedar alineadas, siempre que los nombre tengan menos de veintiun car acteres y las tarifas sean menos de mil millones la hora.
12.3 Directorios
131
12.3. Directorios
Cuando se crea un archivo nuevo abri endolo y escribiendo, este va a quedar en el directorio en uso (aqu el en el que se estuviese al ejecutar el programa). Del mismo modo, cuando se abre un archivo para leerlo, Python lo busca en el directorio en uso. Si usted quiere abrir un archivo de cualquier otro sitio, tiene que especicar la ruta del archivo, que es el nombre del directorio (o carpeta) donde se encuentra este: >>> f = open("/usr/share/dict/words","r") >>> print f.readline() Aarhus Este ejemplo abre un archivo denominado words, que se encuentra en un directorio llamado dict, que est a en share, en usr, que est a en el directorio de nivel superior del sistema, llamado /. No se puede usar / como parte del nombre de un archivo; est a reservado como delimitador entre nombres de archivo y directorios. El archivo /usr/share/dict/words contiene una lista de palabras en orden alfab etico, la primera de las cuales es el nombre de una universidad danesa.
12.4. Encurtido
Para poner valores en un archivo, se deben convertir a cadenas. Usted ya ha visto como hacerlo con str: >>> f.write (str(12.3)) >>> f.write (str([1,2,3])) El problema es que cuando se vuelve a leer el valor, se obtiene una cadena. Se ha perdido la informacion del tipo de dato original. En realidad, no se puede distinguir donde termina un valor y donde comienza el siguiente: >>> f.readline() 12.3[1, 2, 3] La solucion es el encurtido, llamado as porque encurte estructuras de datos. El modulo pickle contiene las ordenes necesarias. Para usarlo, se importa pickle y luego se abre el archivo de la forma habitual: >>> import pickle >>> f = open("test.pck","w")
132
Archivos y excepciones
Para almacenar una estructura de datos, se usa el m etodo dump y luego se cierra el archivo de la forma habitual: >>> pickle.dump(12.3, f) >>> pickle.dump([1,2,3], f) >>> f.close() Ahora podemos abrir el archivo para leer y cargar las estructuras de datos que volcamos ah : >>> f = open("test.pck","r") >>> x = pickle.load(f) >>> x 12.3 >>> type(x) <type float> >>> y = pickle.load(f) >>> y [1, 2, 3] >>> type(y) <type list> Cada vez que invocamos load obtenemos un valor del archivo completo con su tipo original.
12.5. Excepciones
Siempre que ocurre un error en tiempo de ejecucion, se crea una excepcion . Normalmente el programa se para y Python presenta un mensaje de error. Por ejemplo, la division por cero crea una excepcion: >>> print 55/0 ZeroDivisionError: integer division or modulo Un elemento no existente en una lista hace lo mismo: >>> a = [] >>> print a[5] IndexError: list index out of range O el acceso a una clave que no est a en el diccionario: >>> b = {} >>> print b[qu e] KeyError: qu e
12.5 Excepciones
133
En cada caso, el mensaje de error tiene dos partes: el tipo de error antes de los dos puntos y detalles sobre el error despu es de los dos puntos. Normalmente, Python tambi en imprime una traza de donde se encontraba el programa, pero la hemos omitido en los ejemplos. A veces queremos realizar una operacion que podr a provocar una excepcion, pero no queremos que se pare el programa. Podemos manejar la excepcion usando las sentencias try y except. Por ejemplo, podemos preguntar al usuario por el nombre de un archivo y luego intentar abrirlo. Si el archivo no existe, no queremos que el programa se aborte; queremos manejar la excepcion. nombreArch = raw_input(Introduce un nombre de archivo: ) try: f = open (nombreArch, "r") except: print No hay ning un archivo que se llame, nombreArch La sentencia try ejecuta las sentencias del primer bloque. Si no se produce ninguna excepcion, pasa por alto la sentencia except. Si ocurre cualquier excepcion, ejecuta las sentencias de la rama except y despu es continua. Podemos encapsular esta capacidad en una funcion: existe, que acepta un nombre de archivo y devuelve verdadero si el archivo existe y falso si no: def existe(nombreArch): try: f = open(nombreArch) f.close() return True except: return False Se pueden usar multiples bloques except para manejar diferentes tipos de excepciones. El Manual de Referencia de Python contiene los detalles. Si su programa detecta una condicion de error, se puede lanzar (raise en ingl es) una excepcion. Aqu hay un ejemplo que acepta una entrada del usuario y comprueba si es 17. Suponiendo que 17 no es una entrada v alida por cualquier razon, lanzamos una excepcion. def tomaNumero () : # Recuerda, los acentos est an prohibidos x = input (Elige un n umero: ) # en los nombres de funciones y variables! if x == 17 :
134
Archivos y excepciones
umero umeroMalo, 17 es un mal n raise ErrorN return x La sentencia raise acepta dos argumentos: el tipo de excepcion e informacion espec ca acerca del error. ErrorN umeroMalo es un nuevo tipo de excepcion que hemos inventado para esta aplicacion. Si la funcion llamada tomaNumero maneja el error, el programa puede continuar; en caso contrario, Python imprime el mensaje de error y sale: >>> tomaNumero () Elige un n umero: 17 umero umeroMalo: 17 es un mal n ErrorN El mensaje de error incluye el tipo de excepcion y la informacion adicional proporcionada. Como ejercicio, escribe una funci on que use tomaNumero para leer un nume ro del teclado y que maneje la excepci on ErrorNumeroMalo.
12.6. Glosario
Archivo: entidad con nombre, normalmente almacenada en un disco duro, disquete o CD-ROM, que contiene una secuencia de car acteres. Directorio: coleccion de archivos, con nombre, tambi en llamado carpeta. Ruta: secuencia de nombres de directorio que especica la localizacion exacta de un archivo. Archivo de texto: un archivo que contiene car acteres imprimibles organizados en l neas separadas por car acteres de salto de l nea. Sentencia break: es una sentencia que provoca que el ujo de ejecucion salga de un ciclo. Sentencia continue: sentencia que provoca que termine la iteracion actual de un ciclo. El ujo de la ejecucion va al principio del ciclo, evalua la condicion, y procede en consecuencia. Operador de formato: el operador % toma una cadena de formato y una tupla de expresiones y entrega una cadena que incluye las expresiones, formateadas de acuerdo con la cadena de formato. Cadena de formato: una cadena que contiene car acteres imprimibles y secuencias de formato que indican como dar formato a valores.
12.6 Glosario
135
Secuencia de formato: secuencia de car acteres que comienza con % e indica como dar formato a un valor. Encurtir: escribir el valor de un dato en un archivo junto con la informacion sobre su tipo de forma que pueda ser reconstituido m as tarde. Excepcion: error que ocurre en tiempo de ejecucion. Manejar: impedir que una excepcion detenga un programa utilizando las sentencias except y try.
Cap tulo 13
Clases y objetos
13.1. Tipos compuestos denidos por el usuario
Una vez utilizados algunos de los tipos internos de Python, estamos listos para crear un tipo denido por el usuario: el Punto. Piense en el concepto de un punto matem atico. En dos dimensiones, un punto tiene dos numeros (coordenadas) que se tratan colectivamente como un solo objeto. En notacion matem atica, los puntos suelen escribirse entre par entesis con una coma separando las coordenadas. Por ejemplo, (0, 0) representa el origen, y (x, y ) representa el punto x unidades a la derecha e y unidades hacia arriba desde el origen. Una forma natural de representar un punto en Python es con dos valores en punto otante. La cuestion es, entonces, como agrupar esos dos valores en un objeto compuesto. La solucion r apida y burda es utilizar una lista o tupla, y para algunas aplicaciones esa podr a ser la mejor opcion. Una alternativa es que el usuario dena un nuevo tipo de dato compuesto, tambi en llamado una clase. Esta aproximacion exige un poco m as de esfuerzo, pero tiene algunas ventajas que pronto se har an evidentes. Una denicion de clase se parece a esto: class Punto: pass Las deniciones de clase pueden aparecer en cualquier lugar de un programa, pero normalmente est an al principio (tras las sentencias import). Las reglas sint acticas de la denicion de clases son las mismas que para las otras sentencias compuestas. (ver la Seccion 5.5).
138
Clases y objetos
Esta denicion crea una nueva clase llamada Punto. La sentencia pass no tiene efectos; solo es necesaria porque una sentencia compuesta debe tener algo en su cuerpo. Al crear la clase Punto hemos creado un nuevo tipo, que tambi en se llama Punto. Los miembros de este tipo se llaman instancias del tipo u objetos. La creacion de una nueva instancia se llama instanciacion . Para instanciar un objeto Punto ejecutamos una funcion que se llama (probablemente usted ha adivinado) Punto: limpio = Punto() A la variable limpio se le asigna una referencia a un nuevo objeto Punto. A una funcion como Punto que crea un objeto nuevo se le llama constructor.
13.2. Atributos
Podemos anadir nuevos datos a una instancia utilizando la notacion de punto: >>> limpio.x = 3.0 >>> limpio.y = 4.0 Esta sintaxis es similar a la usada para seleccionar una variable de un modulo, como math.pi o string.uppercase. En este caso, sin embargo, estamos seleccionando un dato de una instancia. Estos datos con nombre se denominan atributos. El diagrama de estados que sigue muestra el resultado de esas asignaciones:
limpio x y 3.0 4.0
La variable limpio apunta a un objeto Punto, que contiene dos atributos. Cada atributo apunta a un numero en punto otante. Podemos leer el valor de un atributo utilizando la misma sintaxis: >>> print limpio.y 4.0 >>> x = limpio.x >>> print x 3.0 La expresion limpio.x signica, ve al objeto al que apunta limpio y toma el valor de x. En este caso, asignamos ese valor a una variable llamada x. No hay conicto entre la variable x y el atributo x. El proposito de la notacion de punto es identicar de forma inequ voca a qu e variable se reere el programador. Se puede usar la notacion de punto como parte de cualquier expresion. As , las sentencias que siguen son correctas:
13.3 Instancias como par ametro print ( + str(limpio.x) + , + str(limpio.y) + ) distanciaAlCuadrado = limpio.x * limpio.x + limpio.y * limpio.y La primera l nea presenta (3.0, 4.0); la segunda l nea calcula el valor 25.0. Usted puede estar tentado a imprimir el propio valor de limpio: >>> print limpio <__main__.Point instance at 80f8e70>
139
El resultado indica que limpio es una instancia de la clase Punto que se denio en main . 80f8e70 es el identicador unico de este objeto, escrito en hexadecimal. Probablemente e sta no es la manera m as clara de mostrar un objeto Punto. En breve veremos como cambiar esto. Como ejercicio, cree e imprima un objeto Punto y luego use id para imprimir el identicador unico del objeto. Traduzca el numero hexadecimal a decimal y asegurese de que coincidan.
13.4. Mismidad
El signicado de la palabra mismo parece totalmente claro hasta que uno se detiene a pensarlo un poco y se da cuenta de que hay algo m as de lo que se supone comunmente. Por ejemplo, si alguien dice Pepe y yo tenemos la misma moto, lo que quiere decir es que su moto y la de Pepe son de la misma marca y modelo, pero que son dos motos distintas. Si dice Pepe y yo tenemos la misma madre, quiere decir que
140
Clases y objetos
su madre y la de Pepe son la misma persona1 . As que la idea de identidad es diferente segun el contexto. Cuando uno habla de objetos, hay una ambiguedad parecida. Por ejemplo, si dos Puntos son el mismo, signica que contienen los mismos datos (coordenadas) o que son de verdad el mismo objeto? Para averiguar si dos referencias se reeren al mismo objeto, se utiliza el operador ==. Por ejemplo: >>> p1 = Punto() >>> p1.x = 3 >>> p1.y = 4 >>> p2 = Punto() >>> p2.x = 3 >>> p2.y = 4 >>> p1 == p2 False Aunque p1 y p2 contienen las mismas coordenadas, no son el mismo objeto. Si asignamos p1 a p2, las dos variables son alias del mismo objeto: >>> p2 = p1 >>> p1 == p2 True Este tipo de igualdad se llama igualdad supercial, porque solo compara las referencias, pero no el contenido de los objetos. Para comparar los contenidos de los objetos (igualdad profunda) podemos escribir una funcion llamada mismoPunto: def mismoPunto(p1, p2) : return (p1.x == p2.x) and (p1.y == p2.y) Si ahora creamos dos objetos diferentes que contienen los mismos datos podremos usar mismoPunto para averiguar si representan el mismo punto: >>> >>> >>> >>> >>> p1 = p1.x p1.y p2 = p2.x Punto() = 3 = 4 Punto() = 3
1 No todas las lenguas tienen el mismo problema. Por ejemplo, el alem an tiene palabras diferentes para los diferentes tipos de identidad. Misma moto en este contexto ser a gleiche Motorrad y misma madre ser a selbe Mutter.
141
Por supuesto, si las dos variables apuntan al mismo objeto mismoPunto devuelve verdadero.
142
Clases y objetos
13.8 Copiado
143
Las variables danchura y daltura indican cu anto debe agrandarse el rect angulo en cada direccion. Invocar este m etodo tiene el efecto de modicar el Rectangulo que se pasa como argumento. Por ejemplo, podemos crear un nuevo Rectangulo llamado b y pas arselo a la funcion agrandarRect: >>> >>> >>> >>> >>> >>> >>> b = Rectangulo() b.anchura = 100.0 b.altura = 200.0 b.esquina = Punto() b.esquina.x = 0.0; b.esquina.y = 0.0; agrandarRect(b, 50, 100)
Mientras agrandarRect se est a ejecutando, el par ametro caja es un alias de b. Cualquier cambio que se hagas a caja afectar a tambi en a b. A modo de ejercicio, escriba una funci on llamada mueveRect que tome un Rectangulo y dos par ametros llamados dx y dy. Tiene que cambiar la posici on del rect angulo anadiendo en la esquina: dx a la coordenada x y dy a la coordenada y.
13.8. Copiado
El uso de un alias puede hacer que un programa sea dif cil de leer, porque los cambios hechos en un lugar pueden tener efectos inesperados en otro lugar. Es dif cil estar al tanto de todas las variables que pueden apuntar a un objeto dado. Copiar un objeto es, muchas veces, una alternativa a la creacion de un alias. El modulo copy contiene una funcion llamada copy que puede duplicar cualquier objeto: >>> import copy >>> p1 = Punto() >>> p1.x = 3 >>> p1.y = 4 >>> p2 = copy.copy(p1) >>> p1 == p2 False >>> mismoPunto(p1, p2) True
144
Clases y objetos
Una vez que hemos importado el modulo copy, podemos usar el m etodo copy para hacer un nuevo Punto. p1 y p2 no son el mismo punto, pero contienen los mismos datos. Para copiar un objeto simple como un Punto, que no contiene objetos incrustados, copy es suciente. Esto se llama copiado supercial. Para algo como un Rectangulo, que contiene una referencia a un Punto, copy no lo hace del todo bien. Copia la referencia al objeto Punto, de modo que tanto el Rectangulo viejo como el nuevo apuntan a un unico Punto. Si creamos una caja, b1, de la forma habitual y entonces hacemos una copia, b2, usando copy, el diagrama de estados resultante se ve as :
b1 anchura altura esquina 100.0 200.0 x y 0.0 0.0 100.0 200.0 anchura altura esquina b2
Es casi seguro que esto no es lo que queremos. En este caso, la invocacion de agrandaRect sobre uno de los Rectangulos no afectar a al otro, pero la invocacion de mueveRect sobre cualquiera afectar a a ambos! Este comportamiento es confuso y propicia los errores. Afortunadamente, el modulo copy contiene un m etodo llamado deepcopy que copia no solo el objeto, sino tambi en cualesquiera objetos incrustados en e l. No lo sorprender a saber que esta operacion se llama copia profunda (deep copy). >>> b2 = copy.deepcopy(b1) Ahora b1 y b2 son objetos totalmente independientes. Podemos usar deepcopy para reescribir agrandaRect de modo que en lugar de modicar un Rectangulo existente, cree un nuevo Rectangulo que tiene la misma localizacion que el viejo pero nuevas dimensiones: def agrandaRect(caja, danchura, daltura) : import copy nuevaCaja = copy.deepcopy(caja) nuevaCaja.anchura = nuevaCaja.anchura + danchura nuevaCaja.altura = nuevaCaja.altura + daltura return nuevaCaja
Como ejercicio, reescriba mueveRect de modo que cree y devuelva un nuevo Rectangulo en lugar de modicar el viejo.
13.9 Glosario
145
13.9. Glosario
Clase: tipo compuesto denido por el usuario. Tambi en se puede pensar en una clase como una plantilla para los objetos que son instancias de la misma. Instanciar: Crear una instancia de una clase. Instancia: objeto que pertenece a una clase. Objeto: tipo de dato compuesto que suele usarse para representar una cosa o concepto del mundo real. Constructor: m etodo usado para crear nuevos objetos. Atributo: uno de los elementos de datos con nombre que constituyen una instancia. Igualdad supercial: igualdad de referencias, o dos referencias que apuntan al mismo objeto. Igualdad profunda: igualdad de valores, o dos referencias que apuntan a objetos que tienen el mismo valor. Copia supercial: copiar el contenido de un objeto, incluyendo cualquier referencia a objetos incrustados; implementada por la funcion copy del modulo copy. Copia profunda: copiar el contenido de un objeto as como cualesquiera objetos incrustados, y los incrustados en estos, y as sucesivamente. Est a implementada en la funcion deepcopy del modulo copy.
Cap tulo 14
Clases y funciones
14.1. Hora
Como otro ejemplo de tipo de dato denido por el usuario deniremos una clase llamada Hora: class Hora: pass Ahora podemos crear un nuevo objeto Hora y asignarle atributos para las horas, minutos y segundos: tiempo = Hora() tiempo.hora = 11 tiempo.minutos = 59 tiempo.segundos = 30 El diagrama para el objeto Hora luce as :
tiempo hora minutos segundos 11 59 30
Como ejercicio, escriba una funci on imprimirHora que reciba un objeto Hora como argumento y lo imprima de la forma horas:minutos:segundos. Escriba una funci on booleana despues que reciba dos objetos Hora, t1 y t2 como argumentos, y retorne cierto si t1 va despu es de t2 cronol ogicamente y falso en caso contrario.
148
Clases y funciones
>>> horaComer = sumarHoras(horaActual, horaPan) >>> imprimirHora(horaComer) La salida de este programa es 12:49:30, que est a correcta. Por otro lado, hay casos en los que no funciona bien. Puede pensar en uno? El problema radica en que esta funcion no considera los casos donde el numero de segundos o minutos suman m as de sesenta. Cuando eso ocurre tenemos que acarrear los segundos extra a la columna de minutos. Tambi en puede pasar lo mismo con los minutos. Aqu hay una version correcta:
14.3 Modicadoras def sumarHoras(t1, t2): sum = Hora() sum.hora = t1.hora + t2.hora sum.minutos = t1.minutos + t2.minutos sum.segundos = t1.segundos + t2.segundos if sum.segundos >= 60: sum.segundos = sum.segundos - 60 sum.minutos = sum.minutos + 1 if sum.minutos >= 60: sum.minutos = sum.minutos - 60 sum.hora = sum.hora + 1 return sum
149
Aunque ahora ha quedado correcta, ha empezado a agrandarse. M as adelante sugeriremos un enfoque alternativo que produce un codigo m as corto.
14.3. Modicadoras
A veces es deseable que una funcion modique uno o varios de los objetos que recibe como par ametros. Usualmente, el codigo que hace el llamado a la funcion conserva una referencia a los objetos que est a pasando, as que cualquier cambio que la funcion les haga ser a evidenciado por dicho codigo. Este tipo de funciones se denominan modicadoras. incrementar, que agrega un numero de segundos a un objeto Hora, se escribir a m as naturalmente como funcion modicadora. Un primer acercamiento a la funcion lucir a as : def incrementar(h, segundos): h.segundos = h.segundos + segundos if h.segundos >= 60: h.segundos = h.segundos - 60 h.minutos = h.minutos + 1 if h.minuto >= 60: h.minutos = h.minutos - 60 h.hora = h.hora + 1 return h
150
Clases y funciones
La primera l nea ejecuta la operacion b asica, las siguientes consideran los casos especiales que ya hab amos visto. Es correcta esta funcion? Que pasa si el par ametro segundos es mucho m as grande que sesenta? En ese caso, no solo es suciente anadir uno, tenemos que sumar de uno en uno hasta que segundos sea menor que sesenta. Una solucion consiste en reemplazar las sentencias if por sentencias while: def incrementar(hora, segundos): while hora.segundos >= 60: hora.segundos = hora.segundos - 60 hora.minutos = hora.minutos + 1 while hora.minutos >= 60: hora.minutos = hora.minutos - 60 hora.hora = hora.hora + 1 return hora hora.segundos = hora.segundos + segundos
Ahora, la funcion s es correcta, aunque no sigue el proceso m as eciente. Como ejercicio, reescriba la funci on de forma que no contenga ciclos y siga siendo correcta. Reescriba incrementar como una funci on pura, y escriba llamados a funciones de las dos versiones.
151
152
Clases y funciones
Esta version es mucho m as corta que la original, y es mucho m as f acil de demostrar que es correcta (asumiendo, como de costumbre, que las funciones que llama son correctas). Como ejercicio, reescriba incrementar usando convertirASegundos y crearHora.
14.6. Generalizacion
Desde cierto punto de vista, convertir de base 60 a base 10 y viceversa es m as dif cil que calcular solamente con horas. La conversion de bases es m as abstracta, mientras que nuestra intuicion para manejar horas est a m as desarrollada. Pero si tenemos la intuicion de tratar las horas como numeros en base 60 y hacemos la inversion de escribir las funciones de conversion (convertirASegundos y crearHora), obtenemos un programa m as corto, legible, depurable y conable. Tambi en facilita la adicion de m as caracter sticas. Por ejemplo, piense en el problema de restar dos Horas para averiguar el tiempo que transcurre entre ellas. La solucion ingenua har a resta llevando pr estamos. En cambio, usar las funciones de conversion ser a mas f acil. Ironicamente, algunas veces el hacer de un problema algo m as dif cil (o m as general) lo hace m as f acil (porque hay menos casos especiales y menos oportunidades para caer en errores).
14.7. Algoritmos
Cuando usted escribe una solucion general para una clase de problemas, en vez de encontrar una solucion espec ca a un solo problema, ha escrito un algoritmo. Mencionamos esta palabra antes, pero no la denimos cuidadosamente. No es f acil de denir, as que intentaremos dos enfoques. Primero, considere algo que no es un algoritmo. Cuando usted aprendio a multiplicar d gitos, probablemente memorizo la tabla de multiplicacion. De hecho, usted memorizo 100 soluciones espec cas. Este tipo de conocimiento no es algor tmico. Pero si usted era perezoso, probablemente aprendio a hacer trampa por medio de algunos trucos. Por ejemplo, para encontrar el producto entre n y 9, usted puede escribir n 1 como el primer d gito y 10 n como el segundo. Este truco es una solucion general para multiplicar cualquier d gito por el 9. Este es un algoritmo! Similarmente, las t ecnicas que aprendio para hacer suma con acarreo (llevando valores para la columna hacia la izquierda), resta con pr estamos, y division larga, todas son algoritmos. Una de las caracter sticas de los algoritmos es que no requieren inteligencia para ejecutarse. Son procesos mec anicos en el que cada paso sigue al anterior de acuerdo con un conjunto de reglas sencillas.
14.8 Glosario
153
En nuestra opinion, es vergonzoso que los seres humanos pasemos tanto tiempo en la escuela aprendiendo a ejecutar algoritmos que, literalmente, no requieren inteligencia. Por otro lado, el proceso de disenar algoritmos es interesante, intelectualmente desaante y una parte central de lo que denominamos programacion. Algunas cosas que la gente hace naturalmente sin dicultad o pensamiento consciente, son las mas dif ciles de expresar algor tmicamente. Entender el lenguaje natural es una de ellas. Todos lo hacemos, pero hasta ahora nadie ha sido capaz de explicar como lo hacemos, al menos no con un algoritmo.
14.8. Glosario
Funcion pura: funcion que no modica ninguno de los objetos que recibe como par ametros. La mayor a de las funciones puras son fruct feras. Modicadora: funcion que cambia uno o varios de los objetos que recibe como par ametros. La mayor a de los modicadoras no retornan nada. Estilo de programacion funcional estilo de diseno de programas en el que la mayor a de funciones son puras. Desarrollo con prototipos: es la forma de desarrollar programas empezando con un prototipo que empieza a mejorarse y probarse gradualmente. Desarrollo planeado: es la forma de desarrollar programas que implica un conocimiento de alto nivel sobre el problema y mas planeacion que el desarrollo incremental o el desarrollo con prototipos. Algoritmo: conjunto de instrucciones para resolver una clase de problemas por medio de un proceso mec anico, no inteligente.
Cap tulo 15
Clases y m etodos
15.1. Caracter sticas de orientacion a objetos
Python es un lenguaje de programacion orientado a objetos, lo que quiere decir que proporciona caracter sticas que soportan la programacion orientada a objetos. No es f acil denir la programacion orientada a objetos, pero ya hemos notado algunos de sus elementos clave: Los programas se construyen a partir de deniciones de objetos y deniciones de funciones; la mayor a de los computos se hacen con base en objetos. Cada denicion de objetos corresponde a algun concepto o cosa del mundo real, y las funciones que operan sobre esos objetos corresponden a las maneras en que los conceptos o cosas reales interactuan. Por ejemplo, la clase Hora, denida en el Cap tulo 14, corresponde a la forma en que la gente registra las horas del d a y las funciones que denimos corresponden a la clase de cosas que la gente hace con horas. Similarmente, las clases Punto y Rectangulo corresponden a los conocidos conceptos geom etricos. Hasta aqu , no hemos aprovechado las caracter sticas que Python proporciona para soportar la programacion orientada a objetos. De hecho, estas caracter sticas no son necesarias. La mayor a solo proporciona una sintaxis alternativa para cosas que ya hemos logrado; pero, en muchos casos, esta forma alternativa es m as concisa y comunica de una manera mas precisa la estructura de los programas. Por ejemplo, en el programa Hora no hay una conexion obvia entre la denicion de clase y las deniciones de funciones. Despu es de examinarlo un poco, es evidente que todas las funciones toman como par ametro al menos un objeto Hora.
156
Clases y m etodos
Esta observacion es la motivacion para los m etodos. Ya hemos visto algunos m etodos como keys y values, que llamamos sobre diccionarios. Cada m etodo se asocia con una clase y est a pensado para invocarse sobre instancias de dicha clase. Los m etodos son como las funciones, pero con dos diferencias: Los m etodos se denen adentro de una denicion de clase, a n de marcar expl citamente la relacion entre la clase y e stos. La sintaxis para llamar o invocar un m etodo es distinta que para las funciones. En las siguientes secciones tomaremos las funciones de los cap tulos anteriores y las transformaremos en m etodos. Esta transformacion es totalmente mec anica; se puede llevar a cabo siguiendo una secuencia de pasos. Si usted se siente comodo al transformar de una forma a la otra, ser a capaz de escoger lo mejor de cada lado para resolver los problemas que tenga a la mano.
15.2. imprimirHora
En el cap tulo 14, denimos una clase denominada Hora y usted escribio una funcion denominada imprimirHora, que luc a as : class Hora: pass def imprimirHora(h): print str(h.hora) + ":" + str(h.minutos) + ":" + str(h.segundos) Para llamar esta funcion, le pasamos un objeto Hora como par ametro: >>> >>> >>> >>> >>> horaActual = Hora() horaActual.hora = 9 horaActual.minutos = 14 horaActual.segundos = 30 imprimirHora(horaActual)
Para convertir imprimirHora en un m etodo todo lo que tenemos que hacer es ponerla adentro de la denicion de clase. Note como ha cambiado la indentacion. class Hora: def imprimirHora(h):
157
Ahora podemos llamar a imprimirHora usando la notacion punto. >>> horaActual.imprimirHora() Como de costumbre, el objeto en el que el m etodo se llama aparece antes del punto y el nombre del m etodo va a la derecha. El objeto al cual se invoca el m etodo se asigna al primer par ametro, as que horaActual se asigna al par ametro h. Por convencion, el primer par ametro de un m etodo se denomina self (en ingl es, eso es algo como s mismo). La razon para hacerlo es un poco tortuosa, pero se basa en una met afora muy util. La sintaxis para una llamada de funcion, imprimirHora(horaActual), sugiere que la funcion es el agente activo. Dice algo como Hey, imprimirHora! Aqu hay un objeto para que imprimas. En la programacion orientada a objetos, los objetos son los agentes activos. Una invocacion como horaActual.imprimirHora() dice algo como Hey, objeto horaActual! Por favor, impr mase a s mismo!. Este cambio de perspectiva parece ser solo cortes a, pero puede ser util. En los ejemplos que hemos visto no lo es. Pero, el transferir la responsabilidad desde las funciones hacia los objetos hace posible escribir funciones m as vers atiles y facilita la reutilizacion y el mantenimiento de codigo.
158
Clases y m etodos
if self.minutos >= 60: self.minutos = self.minutos - 60 self.hora = self.hora + 1 return self La transformacion es totalmente mec anica ponemos la denicion del m etodo adentro de la clase y cambiamos el nombre del primer par ametro. Ahora podemos llamar a incrementar como m etodo horaActual.incrementar(500) Nuevamente, el objeto con el cual se invoca el m etodo se asigna al primer par ametro, self. El segundo par ametro, segundos recibe el valor 500. Como ejercicio, convierta convertirASegundos (de la Secci on 14.5) a un m etodo de la clase Hora.
159
Llamamos a este m etodo sobre un objeto y le pasamos el otro como argumento: if horaComer.despues(horaActual): print "El pan estar a listo para comer en un momento." Casi se puede leer el llamado en lenguaje natural:Si la hora para Comer viene despues de la hora Actual, entonces ....
Clases y m etodos
Como ejercicio, anada un cuarto par ametro, fin, que especique hasta donde continuar la busqueda. Advertencia: Este ejercicio tiene una cascarita. El valor por defecto de fin deber a ser len(cad), pero esto no funciona. Los valores por defecto se evaluan en el momento de denici on de las funciones, no cuando se llaman. Cuando se dene buscar, cad no existe todav a, as que no se puede obtener su longitud.
15.7 Reconsiderando la clase Punto >>> horaActual = Hora(9) >>> horaActual.imprimirHora() >>> 9:0:0 O, solo los dos primeros: >>> horaActual = Hora(9, 14) >>> horaActual.imprimirHora() >>> 9:14:0
161
Finalmente, podemos proporcionar algunos par ametros, nombr andolos expl citamente: >>> horaActual = Hora(segundos = 30, hora = 9) >>> horaActual.imprimirHora() >>> 9:0:30
Clases y m etodos
Cuando escribimos una nueva clase, casi siempre empezamos escribiendo init , ya que facilita la instanciacion de objetos, y str , que casi siempre es esencial para la depuracion.
163
Hay varias formas de sobrecargar el comportamiento del operador multiplicacion: deniendo un m etodo mul , o rmul , o ambos. Si el operando izquierdo de * es un Punto, Python invoca a mul , asumiendo que el otro operando tambi en es un Punto. El siguiente m etodo calcula el producto escalar de los dos puntos de acuerdo a las reglas del a lgebra lineal: def __mul__(self, otro): return self.x * otro.x + self.y * otro.y Si el operando izquierdo de * es un tipo primitivo y el operando derecho es un escalar : Punto, Python llama a rmul , que ejecuta la multiplicacion def __rmul__(self, otro): return Punto(otro * self.x,
otro * self.y)
El resultado ahora es un nuevo Punto cuyas coordenadas son multiplos de las originales. Si otro pertenece a un tipo que no se puede multiplicar por un numero de punto otante, la funcion rmul producir a un error. Este ejemplo ilustra las dos clases de multiplicacion: >>> p1 = Punto(3, 4) >>> p2 = Punto(5, 7) >>> print p1 * p2 43 >>> print 2 * p2 (10, 14) Que pasa si tratamos de evaluar p2 * 2? Ya que el primer par ametro es un Punto, Python llama a mul con 2 como segundo argumento. Dentro de mul , el programa intenta acceder al valor x de otro, lo que falla porque un numero entero no tiene atributos: >>> print p2 * 2 AttributeError: int object has no attribute x Desafortunadamente, el mensaje de error es un poco opaco. Este ejemplo demuestra una de las dicultades de la sobrecarga de operadores. Algunas veces es dif cil saber qu e codigo est a ejecut andose.Para un ejemplo completo de sobrecarga de operadores vea el Ap endice B.
164
Clases y m etodos
15.9. Polimorsmo
La mayor a de los m etodos que hemos escrito solo funcionan para un tipo de dato espec co. Cuando se crea un nuevo tipo de objeto, se escriben m etodos que operan sobre ese tipo. Pero hay ciertas operaciones que se podr an aplicar a muchos tipos, un ejemplo de e stas son las operaciones aritm eticas de las secciones anteriores. Si muchos tipos soportan el mismo conjunto de operaciones, usted puede escribir funciones que trabajen con cualquiera de estos tipos. Por ejemplo la operacion multsuma (que se usa en el a lgebra lineal) toma tres par ametros, multiplica los primeros dos y luego suma a esto el tercero. En Python se puede escribir as : def multsuma (x, y, z): return x * y + z Este m etodo funcionar a para cualesquier valores de x e y que puedan multiplicarse, y para cualquier valor de z que pueda sumarse al producto. Podemos llamarla sobre numeros: >>> multsuma (3, 2, 1) 7 O sobre Puntos: >>> p1 = Punto(3, 4) >>> p2 = Punto(5, 7) >>> print multsuma (2, p1, p2) (11, 15) >>> print multsuma (p1, p2, 1) 44 En el primer caso, el Punto se multiplica por un escalar y luego se suma a otro Punto. En el segundo caso, el producto punto produce un valor num erico, as que el tercer par ametro tambi en tiene que ser un numero. Una funcion como e sta, que puede tomar par ametros con tipos distintos se denomina polimorca . Otro ejemplo es la funcion derechoyAlReves, que imprime una lista dos veces, al derecho y al rev es: def derechoyAlReves(l): import copy r = copy.copy(l) r.reverse() print str(l) + str(r)
15.10 Glosario
165
Como el m etodo reverse es una funcion modicadora, tenemos que tomar la precaucion de hacer una copia de la lista antes de llamarlo. De esta forma la lista que llega como par ametro no se modica. Aqu hay un ejemplo que aplica derechoyAlReves a una lista: >>> miLista = [1, 2, 3, 4] >>> derechoyAlReves(miLista) [1, 2, 3, 4][4, 3, 2, 1] Por supuesto que funciona para listas, esto no es sorprendente. Lo que ser a sorprendente es que pudi eramos aplicarla a un Punto. Para determinar si una funcion puede aplicarse a un nuevo tipo de dato usamos la regla fundamental del polimorsmo: Si todas las operaciones adentro de la funcion pueden aplicarse al otro tipo, la funcion puede aplicarse al tipo. Las operaciones que usa el m etodo son copy, reverse, y print. copy funciona para cualquier objeto, y como ya hemos escrito un m etodo str para los Puntos, lo unico que nos falta es el m etodo reverse dentro de la clase Punto: def reverse(self): self.x , self.y = self.y, self.x Entonces podemos aplicar derechoyAlReves a objetos Punto: >>> p = Punto(3, 4) >>> derechoyAlReves(p) (3, 4)(4, 3) El mejor tipo de polimorsmo es el que no se pretend a lograr, aquel en el que se descubre que una funcion escrita puede aplicarse a un tipo, para el cual no se hab a planeado hacerlo.
15.10. Glosario
Lenguaje orientado a objetos: lenguaje que tiene caracter sticas, como las clases denidas por el usuario y la herencia, que facilitan la programacion orientada a objetos. Programacion orientada a objetos: estilo de programacion en el que los datos y las operaciones que los manipulan se organizan en clases y m etodos.
166
Clases y m etodos
M etodo: funcion que se dene adentro de una clase y se llama sobre instancias de e sta. Sobreescribir: reemplazar un valor preexistente. Por ejemplo, se puede reemplazar un par ametro por defecto con un argumento particular y un m etodo ya denido, proporcionando un nuevo m etodo con el mismo nombre. M etodo de inicializacion: m etodo especial que se llama autom aticamente cuando se crea un nuevo objeto. Inicializa los atributos del objeto. Sobrecarga de operadores: extender el signicado de los operadores primitivos (+, -, *, >, <, etc.) de forma que acepten tipos denidos por el usuario. Producto punto: operacion del a lgebra lineal que multiplica dos Puntos y produce un valor num erico. Multiplicacion escalar: operacion del a lgebra lineal que multiplica cada una de las coordenadas de un Punto por un valor num erico. Polimorca: funcion que puede operar sobre varios tipos de datos. Si todas las operaciones que se llaman dentro de la funcion se le pueden aplicar al tipo de dato, entonces la funcion puede aplic arsela al tipo.
Cap tulo 16
Conjuntos de objetos
16.1. Composicion
En este momento usted ya ha visto varios ejemplos de composicion. Uno de los primeros fue una invocacion de un m etodo como parte de una expresion. Otro ejemplo es la estructura anidada de sentencias; por ejemplo, se puede colocar una sentencia if dentro de un ciclo while, dentro de otra sentencia if. Despu es de observar esto y haber aprendido sobre listas y objetos no deber a sorprenderse al saber que se pueden crear listas de objetos. Tambi en pueden crearse objetos que contengan listas (como atributos), listas que contengan listas, objetos que contengan objetos, y as sucesivamente. En este cap tulo y el siguiente, mostraremos algunos ejemplos de estas combinaciones, usando objetos Carta.
168
Conjuntos de objetos
de esta implementacion es que no ser a tan f acil comparar cartas para ver cu al tiene un valor mayor o una gura mayor. Una alternativa consiste en usar enteros para codicar los valores y las guras. Por codicar, no estamos haciendo alusion a encriptar o traducir a un codigo secreto. Lo que un cient co de la computacion considera codicar es denir una correspondencia entre una secuencia de numeros y los objetos que deseamos representar. Por ejemplo: Picas 3 Corazones 2 Diamantes 1 Tr eboles 0 Una caracter stica notable de esta correspondencia es que las guras aparecen en orden decreciente de valor as como los enteros van disminuyendo. De esta forma, podemos comparar guras mediante los enteros que las representan. Una correspondencia para los valores es bastante sencilla; cada numero se relaciona con el entero correspondiente, y para las cartas que se representan con letras tenemos lo siguiente: A 1 J 11 Q 12 K 13 La razon para usar notacion matem atica en estas correspondencias es que ellas no hacen parte del programa en Python. Son parte del diseno, pero nunca aparecen expl citamente en el codigo fuente. La denicion de la clase Carta luce as : class Carta: def __init__(self, figura=0, valor=0): self.figura = figura self.valor = valor Como de costumbre, proporcionamos un m etodo de inicializacion que toma un par ametro opcional para cada atributo. Para crear un objeto que represente el 3 de tr eboles, usamos este comando: tresTreboles = Carta(0, 3) El primer argumento, 0, representa la gura (tr eboles).
169
forma natural de hacerlo es con listas de cadenas de texto. Asignamos estas listas a atributos de clase al principio de la clase: class Carta: listaFiguras = ["Treboles", "Diamantes", "Corazones", "Picas"] listaValores = ["narf", "As", "2", "3", "4", "5", "6", "7","8", "9", "10", "Jota", "Reina", "Rey"] # se omite el metodo init def __str__(self): return (self.listaFiguras[self.valor] + " de " + self.listaValores[self.figura]) Un atributo de clase se dene afuera de los m etodos y puede ser accedido desde cualquiera de ellos. Dentro de str , podemos usar a listaFiguras y listaValores para establecer una correspondencia entre los valores num ericos de figura, valor y los nombres de las cartas. La expresion self.listaFiguras[self.figura] signica use el atributo figura del objeto self como ndice dentro del atributo de clase listaFiguras, esto seleccionar a la cadena de texto apropiada. La razon para el "narf" en el primer elemento de listaValores consiste en ocupar el elemento cero de la lista que no va a ser usado en el programa. Los valores v alidos van de 1 a 13. Este elemento desperdiciado no es necesario, podr amos haber empezado a contar desde 0, pero es menos confuso codicar 2 como 2, 3 como 3 ... y 13 como 13. Con los m etodos que tenemos hasta aqu , podemos crear e imprimir cartas: >>> c1 = Carta(1, 11) >>> print c1 Jota de Diamantes Los atributos de clase como listaFiguras se comparten por todos los objetos Carta. La ventaja de esto es que podemos usar cualquier objeto Carta para acceder a ellos: >>> c2 = Carta(1, 3) >>> print c2 3 de Diamantes >>> print c2.listaFiguras[1] Diamantes
170
Conjuntos de objetos
La desventaja es que si modicamos un atributo de clase, afecta a todas las otras instancias de la clase. Por ejemplo, si decidimos que Jota de Diamantes deber a llamarse Caballero de Rombos rojos, podr amos ejecutar: >>> c1.listaFiguras[1] = "Caballero de Rombos rojos" >>> print c1 Caballero de Rombos rojos El problema es que todos los Diamantes ahora son Rombos rojos: >>> print c2 3 de Rombos rojos Usualmente no es una buena idea modicar los atributos de clase.
16.5 Mazos def __cmp__(self, otro): # chequea las figuras if self.figura > otro.figura: return 1 if self.figura < otro.figura: return -1 # Si tienen la misma figura... if self.valor > otro.valor: return 1 if self.valor < otro.valor: return -1 # si los valores son iguales... hay un empate return 0 Con este orden los Ases valen menos que los Dos. Como ejercicio, modique que los reyes. cmp para que los Ases tengan mayor puntaje
171
16.5. Mazos
Ahora que tenemos objetos para representar Cartas, el siguiente paso logico consiste en denir una clase para representar un Mazo. Por supuesto, un mazo (o baraja) est a compuesto por cartas, as que cada instancia de Mazo contendr a como atributo una lista de cartas. La siguiente es la denicion de la clase Mazo. El m etodo de inicializacion crea el atributo cartas y genera el conjunto usual de cincuenta y dos cartas: class Mazo: def __init__(self): self.cartas = [] for figura in range(4): for valor in range(1, 14): self.cartas.append(Carta(figura, valor)) La forma m as sencilla de llenar el mazo consiste en usar un ciclo anidado. El ciclo exterior enumera las guras de 0 a 3. El ciclo interno enumera los valores de 1 a 13. Como el ciclo exterior itera cuatro veces y el interno itera trece veces, el numero total de iteraciones es cincuenta y dos (4 13). Cada iteracion crea una nueva instancia de Carta y la pega a la lista cartas. El m etodo append acepta secuencias mutables como las listas y no acepta tuplas.
172 class Mazo: ... def imprimirMazo(self): for carta in self.cartas: print carta
Conjuntos de objetos
En este ejemplo y en los que siguen, los puntos suspensivos indican que hemos omitido los otros m etodos de la clase. Otra alternativa a imprimirMazo puede ser escribir un m etodo str para la clase Mazo. La ventaja de str radica en su mayor exibilidad. Adem as de imprimir el contenido del objeto, genera una representacion de e l en una cadena de texto que puede manipularse en otros lugares del programa, incluso antes de imprimirse. A continuacion hay una version de str que retorna una representacion de un Mazo. Para anadir un estilo de cascada, cada carta se imprime un espacio mas hacia la derecha que la anterior: class Mazo: ... def __str__(self): s = "" for i in range(len(self.cartas)): s = s + " "*i + str(self.cartas[i]) + "\n" return s Este ejemplo demuestra varios puntos. Primero, en vez de recorrer los elementos de la lista self.cartas, estamos usando a i como variable de ciclo que lleva la posicion de cada elemento en la lista de cartas. Segundo, estamos usando el operador multiplicacion aplicado a un numero y una cadena, de forma que la expresion " "*i produce un numero de espacios igual al valor actual de i. Tercero, en vez de usar el comando print para realizar la impresion, utilizamos la funcion str. Pasar un objeto como argumento a str es equivalente a invocar el m etodo str sobre el objeto. Finalmente, estamos usando a la variable s como acumulador. Inicialmente s es la cadena vac a. En cada iteracion del ciclo se genera una nueva cadena y se concatena con el valor viejo de s para obtener el nuevo valor. Cuando el ciclo naliza, s contiene la representacion completa del Mazo, que se despliega (parcialmente) as : >>> mazo = Mazo() >>> print mazo
16.7 Barajando el mazo As de Picas 2 de Picas 3 de Picas 4 de Picas 5 de Picas 6 de Picas 7 de Picas 8 de Picas 9 de Picas 10 de Picas J de Picas Reina de Picas Rey de Picas As de Diamantes
173
Aunque el resultado se despliega en 52 l neas, es una sola cadena que contiene car acteres nueva linea (\n).
174
Conjuntos de objetos
def barajar(self): import random nCartas = len(self.cartas) for i in range(nCartas): j = random.randrange(i, nCartas) self.cartas[i], self.cartas[j] = self.cartas[j], self.cartas[i] En vez de asumir que hay 52 cartas en el mazo, obtenemos el numero de ellas a trav es de la funcion len y lo almacenamos en la variable nCartas. Para cada carta en el mazo, escogemos, aleatoriamente, una carta de las que no han sido barajadas todav a. Intercambiamos la carta actual (con ndice i) con la seleccionada (con ndice j). Para intercambiar las cartas usamos asignacion de tuplas, como en la seccion 10.2: self.cartas[i], self.cartas[j] = self.cartas[j], self.cartas[i] Como ejercicio, reescriba este intercambio sin usar asignaci on de tuplas.
175
En realidad, pop elimina la ultima carta de la lista, as que realmente estamos entreganado cartas por la parte inferior, y esto no causa ningun inconveniente. Una operacion m as que podemos requerir es la funcion booleana estaVacio, que retorna True si el mazo est a vac o: class Mazo: ... def estaVacio(self): return (len(self.cartas) == 0)
16.9. Glosario
Codicar: representar un conjunto de valores usando otro conjunto de valores estableciendo una correspondencia entre ellos. Atributo de clase: variable de una clase que est a fuera de todos los m etodos. Puede ser accedida desde todos los m etodos y se comparte por todas las instancias de la clase. Acumulador: variable que se usa para acumular una serie de valores en un ciclo. Por ejemplo, concatenar varios valores en una cadena o sumarlos.
Cap tulo 17
Herencia
17.1. Denicion
La caracter stica m as asociada con la programacion orientada a objetos quiz as sea la herencia. Esta es la capacidad de denir una nueva clase que constituye la version modicada de una clase preexistente. La principal ventaja de la herencia consiste en que se pueden agregar nuevos m etodos a una clase sin modicarla. El nombre herencia se usa porque la nueva clase hereda todos los m etodos de la clase existente. Extendiendo esta met afora, la clase preexistente se denomina la clase madre. La nueva clase puede llamarse clase hija o, subclase. La herencia es muy poderosa. Algunos programas complicados se pueden escribir de una manera m as sencilla y compacta a trav es del uso de la herencia. Adem as, facilita la reutilizacion de codigo, ya que se puede especializar el comportamiento de una clase madre sin modicarla. En algunos casos, las relaciones entre las clases reejan la estructura de las entidades del mundo real que se presentan en un problema, y esto hace que los programas sean m as comprensibles. Por otra parte, la herencia puede dicultar la lectura de los programas. Cuando un m etodo se llama, puede que no sea muy claro donde est a denido. El codigo relevante puede estar disperso entre varios modulos. Adem as, muchas soluciones que se pueden escribir con el uso de herencia, tambi en se pueden escribir sin ella, y de una manera elegante (algunas veces m as elegante). Si la estructura natural de las entidades que intervienen en un problema no se presta a pensar en t erminos de herencia, este estilo de programacion puede causar m as perjuicios que benecios. En este cap tulo demostraremos el uso de la herencia como parte de un programa que implementa el juego de cartas La Solterona. Una de las metas es escribir una base de codigo que pueda reutilizarse para implementar otros juegos de cartas.
178
Herencia
179
class Mazo: ... def repartir(self, manos, nCartas=999): nManos = len(manos) for i in range(nCartas): if self.estaVacio(): break # se detiene si no hay cartas # toma la carta en el tope carta = self.eliminarCarta() # siguiente turno! mano = manos[i % nManos] # agrega la carta a la mano mano.agregarCarta(carta)
El segundo par ametro, nCartas, es opcional; y por defecto es un numero entero grande que garantice que todas las cartas del mazo se repartan. La variable de ciclo i va de 0 a nCartas-1. En cada iteracion remueve una carta al tope del mazo usando el m etodo pop, que elimina y retorna el ultimo elemento de la lista. El operador residuo ( %) nos permite repartir cartas de manera circular (una carta a una mano distinta en cada iteracion). Cuando i es igual al numero de manos en la lista, la expresion i % nManos da la vuelta retornando la primera posicion de la lista (la posicion 0).
180
Herencia
181
182 def quitarPareja(self): conteo = 0 cartasOriginales = self.cartas[:] for carta in cartasOriginales: m = Carta(3 - carta.figura, carta.valor) if pareja in self.cartas: self.cartas.remove(carta) self.cartas.remove(m) print "Mano %s: %s se empareja con %s" % (self.name,carta,m) cont = cont + 1 return cont
Herencia
Empezamos haciendo una copia de la lista de cartas, de forma que podamos recorrerla y simultaneamente eliminar cartas. Como self.cartas se modica en el ciclo, no vamos a utilizarlo para controlar el recorrido. Python puede confundirse totalmente si empieza a recorrer una lista que est a cambiando! Para cada carta en la mano, averiguamos si se empareja con una carta escogida de la mano de otra persona. Para esto, tienen que tener el mismo valor y la otra gura del mismo color. La expresion 3 - carta.figura convierte un tr ebol (gura 0) en una Pica (gura 3) y a un Diamante (gura 1) en un Corazon (gura 2). Usted puede comprobar que las operaciones inversas tambi en funcionan. Si hay una carta que se empareje, las dos se eliminan. El siguiente ejemplo demuestra como usar quitarPareja: >>> juego = JuegoDeCartas() >>> mano = ManoSolterona("frank") >>> juego.mazo.repartir([mano], 13) >>> print mano Mano frank contiene As de Picas 2 de Diamantes 7 de Picas 8 de Treboles 6 de Corazones 8 de Picas 7 de Treboles Reina de Treboles 7 de Diamantes 5 de Treboles Jota de Diamantes 10 de Diamantes 10 de Corazones
183
>>> mano.quitarPareja() Mano frank: 7 de Picas se empareja con 7 de Treboles Mano frank: 8 de Picas se empareja con 8 de Treboles Mano frank: 10 de Diamantes se empareja con 10 de Corazones >>> print mano Mano frank contiene Ace de Picas 2 de Diamantes 6 de Corazones Reina de Treboles 7 de Diamantes 5 de Treboles Jota de Diamantes Tenga en cuenta que no hay m etodo init en la clase ManoSolterna. Lo heredamos de Mano.
184
Herencia parejas = self.eliminarParejas() print "---------- Parejas descartadas, comienza el juego" self.imprimirManos() # jugar hasta que las 50 cartas sean descartadas turno = 0 numManos = len(self.manos) while parejas < 25: parejas = parejas+self.jugarUnTurno(turno) turno = (turno + 1) % numManos print "-------- Juego terminado" self.printManos()
Algunos de las etapas del juego se han separado en m etodos. eliminarParejas recorre la lista de manos invocando eliminarPareja en cada una de ellas: class JuegoSolterona(JuegoDeCartas): ... def eliminarParejas(self): count = 0 for mano in self.manos: cont = cont + mano.eliminarParejas() return count Como ejercicio, escriba imprimaManos que recorre la lista self.manos e imprime cada mano. cont es un acumulador que lleva cuenta del numero de parejas que se encontraron en cada mano. Cuando el numero total de parejas encontradas llega a ser veinticinco, se han quitado cincuenta cartas de las manos, y esto implica que la unica carta que resta es la reina de Picas y que el juego ha terminado. La variable turno lleva la pista de cual es el jugador que tiene el turno para jugar. Empieza en 0 y se incrementa de uno en uno; cuando alcanza el valor numManos, el operador residuo lo reinicia en 0. El m etodo jugarUnTurno toma un par ametro que indica el jugador que tiene el turno. El valor de retorno es el numero de parejas encontradas durante este turno: class JuegoSolterona(JuegoDeCartas):
17.7 La clase JuegoSolterona ... def jugarUnTurno(self, i): if self.manos[i].estaVacia(): return 0 vecino = self.encontrarVecino(i) cartaEscogida = self.manos[vecino].eliminarCarta() self.manos[i].agregarCarta(cartaEscogida) print "Mano", self.manos[i].nombre, "escogi o", cartaEscogida cont = self.manos[i].eliminarParejas() self.manos[i].barajar() return cont
185
Si la mano de un jugador est a vac a, este jugador est a fuera del juego, as que no hace ninguna accion y retorna 0. Si esto no es as , un turno consiste en encontrar el primer jugador en la izquierda que tenga cartas, tomar una de e l, y buscar por parejas. Antes de retornar, las cartas en la mano se barajan para que la eleccion del siguiente jugador sea al azar. El m etodo encontrarVecino comienza con el jugador a la izquierda y continua buscando de manera circular hasta que encuentra un jugador que tenga cartas: class JuegoSolterona(JuegoDeCartas): ... def encontrarVecino(self, i): numManos = len(self.manos) for siguiente in range(1,numManos): vecino = (i + siguiente) % numManos if not self.manos[vecino].estaVacia(): return vecino Si encontrarVecino diera toda la vuelta sin encontrar cartas, retornar a None y causar a un error en algun otro lugar del programa. Afortunadamente, usted puede comprobar que esto nunca va a pasar (siempre y cuando el n del juego se detecte correctamente). Hemos omitido el m etodo imprimaManos. Usted puede escribirlo. La siguiente salida es de un juego en el que solo las primeras 15 cartas mas altas (con valor 10 y superior) se repartieron a tres jugadores. Con este pequeno mazo, el juego termina despu es de siete parejas encontradas, en vez de veinticinco. >>> import cartas >>> juego = cartas.JuegoSolterona() >>> juego.jugar(["Allen","Jeff","Chris"]) ---------- Las cartas se han repartido
186 Mano Allen contiene Rey de Corazones Jota de Treboles Reina de Picas Rey de Picas 10 de Diamantes Mano Jeff contiene Reina de Corazones Jota de Picas Jota de Corazones Rey de Diamantes Reina de Diamantes Mano Chris contiene Jota de Diamantes Rey de Treboles 10 de Picas 10 de Corazones 10 de Treboles
Herencia
Mano Jeff: Reina de Corazones se empareja con Reina de Diamantes Mano Chris: 10 de Picas se empareja con 10 de Treboles ---------- Parejas eliminadas, comienza el juego Mano Allen contiene Rey de Corazones Jota de Treboles Reina de Picas Rey de Picas 10 de Diamantes Mano Jeff contiene Jota de Picas Jota de Corazones Rey de Diamantes Mano Jota Rey 10 Chris contiene de Diamantes de Treboles de Corazones
17.8 Glosario
187
Mano Allen escogio Rey de Diamantes Mano Allen: Rey de Corazones se empareja con Rey de Diamantes Mano Jeff escogio 10 de Corazones Mano Chris escogio Jota de Treboles Mano Allen escogio Jota de Corazones Mano Jeff escogio Jota de Diamantes Mano Chris escogio Reina de Picas Mano Allen escogio Jota de Diamantes Mano Allen: Jota de Corazones se empareja con Jota de Diamantes Mano Jeff escogio Rey de Treboles Mano Chris escogio Rey de Picas Mano Allen escogio 10 de Corazones Mano Allen: 10 de Diamantes se empareja con 10 de Corazones Mano Jeff escogio Reina de Picas Mano Chris escogio Jota de Picas Mano Chris: Jota de Treboles se empareja con Jota de Picas Mano Jeff escogio Rey de Picas Mano Jeff: Rey de Treboles se empareja con Rey de Picas ---------- Game is Over Mano Allen esta vacia Mano Jeff contiene Reina de Picas Mano Chris esta vacia
17.8. Glosario
Herencia: es la capacidad de denir una clase, modicando una clase denida previamente. Clase madre: esta es la clase de la que una clase hereda. Clase hija: nueva clase creada por medio de herencia, tambi en recibe el nombre de subclase.
Cap tulo 18
Listas enlazadas
18.1. Referencias incrustadas
Hemos visto ejemplos de atributos (denominados referencias incrustadas) que se reeren a otros objetos en la seccion 13.8. Una estructura de datos muy comun (la lista enlazada), toma ventaja de esta posibilidad. Las listas enlazadas est an hechas de nodos, que contienen una referencia al siguiente nodo en la lista. Adem as, cada nodo contiene una informacion denominada la carga. Una lista enlazada se considera como una estructura de datos recursiva si damos la siguiente denicion. Una lista enlazada es: la lista vac a, representada por el valor None, o un nodo que contiene una carga y una referencia a una lista enlazada. Las estructuras de datos recursivas se implementan naturalmente con m etodos recursivos.
Listas enlazadas
Los par ametros para el m etodo de inicializacion son opcionales. Por defecto la carga y el enlace siguiente, reciben el valor None. La representacion textual de un nodo es la representacion de la carga. Como cualquier valor puede ser pasado a la funcion str , podemos almacenar cualquier tipo de valor en la lista. Para probar la implementacion, podemos crear un Nodo e imprimirlo: >>> nodo = Nodo("test") >>> print nodo test Para hacerlo m as interesante, vamos a pensar en una lista con varios nodos: >>> nodo1 = Nodo(1) >>> nodo2 = Nodo(2) >>> nodo3 = Nodo(3) Este codigo crea tres nodos, pero todav a no tenemos una lista porque estos no estan enlazados. El diagrama de estados luce as :
nodo1 nodo2 nodo3
carga siguiente
1 None
carga siguiente
2 None
carga siguiente
3 None
Para enlazar los nodos, tenemos que lograr que el primer nodo se reera al segundo, y que el segundo se reera al tercero: >>> nodo1.siguiente = nodo2 >>> nodo2.siguiente = nodo3 La referencia del tercer nodo es None, lo que indica que es el ultimo nodo de la lista. Ahora el diagrama de estados luce as :
nodo1 nodo2 nodo3
carga siguiente
carga siguiente
carga siguiente
3 None
Ahora usted sabe como crear nodos y enlazarlos para crear listas. Lo que todav a no est a claro, es el por qu e hacerlo.
191
carga siguiente
carga siguiente
carga siguiente
3 None
nodo
Por convenci on, las listas se imprimen entre corchetes y los elementos se separan por medio de comas, como en el ejemplo [1, 2, 3]. Como ejercicio modique imprimirLista de forma que muestre la salida en este formato.
192
Listas enlazadas
193
carga siguiente
carga siguiente
Si llamamos a imprimirLista sobre esta lista, iterar a para siempre. Si llamamos a imprimirAlReves, se har a recursion hasta causar un error en tiempo de ejecucion. Este comportamiento hace a las listas circulares muy dif ciles de manipular. Sin embargo, a veces son muy utiles. Por ejemplo, podemos representar un numero como una lista de d gitos y usar una lista innita para representar una fraccion periodica. As que no es posible demostrar que imprimirLista e imprimirAlReves terminen. Lo mejor que podemos hacer es probar la sentencia, Si la lista no tiene referencias hacia atr as, los m etodos terminar an.. Esto es una precondicion . Impone una restriccion sobre los par ametros y describe el comportamiento del m etodo si e sta se cumple. M as adelante veremos otros ejemplos.
194
Listas enlazadas
Podr amos haber escrito imprimirAlReves de una manera m as concisa sin las variables cabeza y resto, pero esto tambi en diculta su lectura: def imprimirAlReves(lista) : if lista == None : return imprimirAlReves(lista.siguiente) print lista, Cuando leamos el codigo, tenemos que recordar que imprimirAlReves trata a su argumento como una coleccion y print como a un solo nodo. El teorema de la ambiguedad fundamental describe la ambiguedad inherente en la referencia a un nodo: Una variable que se reera a un nodo puede tratar el nodo como un objeto unico o como el acceso a la lista de nodos
18.8 Funciones facilitadoras y auxiliares 2 >>> imprimirLista(nodo1) 1 3 Este diagrama de estado muestra el efecto de la operacion: primero segundo
195
carga siguiente
carga siguiente
carga siguiente
3 None
Qu e pasa si usted llama este m etodo con una lista que contiene un solo elemento (un singleton)? Qu e pasa si se llama con la lista vac a como argumento? Hay precondiciones para este m etodo? Si las hay, corr jalo de forma que maneje de manera razonable las violaciones a la precondicion.
196
Listas enlazadas
18.10 Invariantes class ListaEnlazada: ... def agregarAlPrincipio(self, carga): nodo = Nodo(carga) nodo.siguiente = self.cabeza self.cabeza = nodo self.numElementos = self.numElementos + 1
197
Como de costumbre, usted debe revisar este codigo para vericar qu e sucede con los casos especiales. Por ejemplo, qu e pasa si se llama cuando la lista est a vac a?
18.10. Invariantes
Algunas listas est an bien formadas. Por ejemplo, si una lista contiene un ciclo, causar a problemas graves a nuestros m etodos, as que deseamos evitar a toda costa que las listas tengan ciclos. Otro requerimiento de las listas es que el numero almacenado en el atributo numElementos de la clase ListaEnlazada sea igual al numero de elementos en la lista. Estos requerimientos se denominan Invariantes porque, idealmente, deber an ser ciertos para todo objeto de la clase en todo momento. Es una muy buena pr actica especicar los Invariantes para los objetos porque permite comprobar de manera mas sencilla la correccion del codigo, revisar la integridad de las estructuras de datos y detectar errores. Algo que puede confundir acerca de los invariantes es que hay ciertos momentos en que son violados. Por ejemplo, en el medio de agregarAlPrincipio, despu es de que hemos agregado el nodo, pero antes de incrementar el atributo numElementos, el Invariante se viola. Esta clase de violacion es aceptable, de hecho, casi siempre es imposible modicar un objeto sin violar un Invariante, al menos moment aneamente. Normalmente, requerimos que cada m etodo que viole un invariante, lo establezca nuevamente. Si hay una parte signicativa de codigo en la que el Invariante se viola, es importante documentarlo claramente, de forma que no se ejecuten operaciones que dependan del Invariante.
18.11. Glosario
Referencia incrustada: referencia almacenada en un atributo de un objeto. Lista enlazada: es la estructura de datos que implementa una coleccion por medio de una secuencia de nodos enlazados.
198
Listas enlazadas
Nodo: elemento de la lista, usualmente implementado como un objeto que contiene una referencia hacia otro objeto del mismo tipo. Carga: dato contenido en un nodo. Enlace: referencia incrustada, usada para enlazar un objeto con otro. Precondicion: condicion logica (o asercion) que debe ser cierta para que un m etodo funcione correctamente. Teorema fundamental de la ambiguedad: la referencia a un nodo de una lista puede interpretarse hacia un nodo determinado o como la referencia a toda la lista de nodos. Singleton: lista enlazada con un solo nodo. Facilitador: m etodo que actua como intermediario entre alguien que llama un m etodo y un m etodo auxiliar. Se crean normalmente para facilitar los llamados y hacerlos menos propensos a errores. M etodo auxiliar: es un m etodo que el programador no llama directamente, sino que es usado por otro m etodo para realizar parte de una operacion. Invariante: asercion que debe ser cierta para un objeto en todo momento (excepto cuando el objeto est a siendo modicado).
Cap tulo 19
Pilas
19.1. Tipos abstractos de datos
Los tipos de datos que ha visto hasta el momento son concretos, en el sentido que hemos especicado completamente como se implementan. Por ejemplo, la clase Carta representa una carta por medio de dos enteros. Pero esa no es la unica forma de representar una carta; hay muchas representaciones alternativas. Un tipo abstracto de datos, o TAD, especica un conjunto de operaciones (o m etodos) y la sem antica de las operaciones (lo que hacen), pero no especica la implementacion de las operaciones. Eso es lo que los hace abstractos. Qu e es lo que los hace tan utiles? La tarea de especicar un algoritmo se simplica si se pueden denotar las operaciones sin tener que pensar al mismo tiempo como se implementan. Como usualmente hay muchas formas de implementar un TAD, puede ser provechoso escribir un algoritmo que pueda usarse con cualquier implementacion alternativa. Los TADs bien conocidos, como el TAD Pila de este cap tulo, a menudo se encuentran implementados en las bibliotecas est andar de los lenguajes de programacion, as que pueden escribirse una sola vez y usarse muchas veces. Las operaciones de los TADs nos proporcionan un lenguaje de alto nivel para especicar algoritmos. Cuando hablamos de TADs hacemos la distincion entre el codigo que utiliza el TAD, denominado codigo cliente, del codigo que implementa el TAD, llamado codigo proveedor.
200
Pilas
201
Una objeto Pila contiene un atributo llamado items que es una lista de los objetos que est an en la Pila. El m etodo de inicializacion le asigna a items la lista vac a. Para meter un nuevo objeto en la Pila, meter lo pega a items. Para sacar un objeto de la Pila, sacar usa al m etodo pop que proporciona Python para eliminar el ultimo elemento de una lista. Finalmente, para vericar si la Pila est a vac a, estaVacia compara a items con la lista vac a. Una implementacion como e sta, en la que los m etodos son simples llamados de m etodos existentes, se denomina barniz. En la vida real, el barniz es una delgada capa de proteccion que se usa algunas veces en la fabricacion de muebles para ocultar la calidad de la madera que recubre. Los cient cos de la computacion usan esta met afora para describir una pequena porcion de codigo que oculta los detalles de una implementacion para proporcionar una interfaz m as simple o m as estandarizada.
Podemos usar los m etodos estaVacia y sacar para eliminar e imprimir todos los objetos en la Pila: while not s.estaVacia() : print s.sacar(), La salida es + 45 54. En otras palabras, acabamos de usar la Pila para imprimir los objetos al rev es, y de una manera muy sencilla! Compare esta porcion de codigo con la implementacion de imprimirAlReves en la Seccion 18.4. Hay una relacion muy profunda e interesante entre la version recursiva de imprimirAlReves y el ciclo anterior. La diferencia reside en que imprimirAlReves usa la Pila que provee el ambiente de ejecucion de Python para llevar pista de los nodos mientras recorre la lista, y luego los imprime cuando la recursion se empieza a devolver. El ciclo anterior hace lo mismo, pero expl citamente por medio de un objeto Pila.
202
Pilas
203
En este caso, el delimitador es el caracter espacio, as que la cadena se separa cada vez que se encuentra un espacio. La funcion re.split es mas poderosa, nos permite especicar una expresion regular en lugar de un delimitador. Una expresion regular es una forma de especicar un conjunto de cadenas. Por ejemplo, [A-Z] es el conjunto de todas las letras y [0-9] es el conjunto de todos los numeros. El operador niega un conjunto, as que [0-9] es el conjunto complemento al de numeros (todo lo que no es un numero), y esto es exactamente lo que deseamos usar para separar una expresion postja: >>> import re >>> re.split("([0-9])", "123+456*/") [123, +, 456, *, , /, ] Observe que el orden de los argumentos es diferente al que se usa en la funcion string.split; el delimitador va antes de la cadena. La lista resultante contiene a los operandos 123 y 456, y a los operadores * y /. Tambi en incluye dos cadenas vac as que se insertan despu es de los operandos.
Pilas
La primera condicion ignora los espacios y las cadenas vac as. Las siguientes dos condiciones detectan los operadores. Asumimos por ahora intr epidamente, que cualquier caracter no num erico es un operando. Veriquemos la funcion evaluando la expresion (56+47)*2 en notacion postja: >>> print evalPostfija ("56 47 + 2 *") 206 Bien, por ahora.
19.9. Glosario
Tipo Abstracto de Datos (TAD): Un tipo de datos (casi siempre es una coleccion de objetos) que se dene por medio de un conjunto de operaciones y que puede ser implementado de diferentes formas. Interfaz: conjunto de operaciones que dene al TAD. Implementacion: codigo que satisface los requerimientos sint acticos y sem anticos de una interfaz de un TAD. Cliente: un programa (o la persona que lo escribio) que usa un TAD. Proveedor: el codigo (o la persona que lo escribio) que implementa un TAD.
19.9 Glosario
205
Barniz: una denicion de clase que implementa un TAD con m etodos que son llamados a otros m etodos, a menudo realizando unas transformaciones previas. El barniz no realiza un trabajo signicativo, pero s mejora o estandariza las interfaces a las que accede el cliente. Estructura de datos gen erica: estructura de datos que puede contener objetos de todo tipo. Inja: una forma de escribir expresiones matem aticas con los operadores entre los operandos. Postja: una forma de escribir expresiones matem aticas con los operadores despu es de los operandos. An alisis sint actico: leer una cadena de car acteres o lexemas y analizar su estructura gramatical. Lexema: conjunto de car acteres que se considera como una unidad para los proposi tos del an alisis sint actico, tal como las palabras del lenguaje natural. Delimitador: caracter que se usa para separar lexemas, tal como los signos de puntuacion en el lenguaje natural.
Cap tulo 20
Colas
Este cap tulo presenta dos TADs: la cola y la cola de prioridad. En la vida real una Cola es una l nea de clientes esperando por algun servicio. En la mayor a de los casos, el primer cliente en la l nea es el proximo en ser atendido. Sin embargo, hay excepciones. En los aeropuertos, los clientes cuyos vuelos est an proximos a partir se atienden, sin importar su posicion en la cola. En los supermercados, un cliente cort es puede dejar pasar a alguien que va a pagar unos pocos v veres. La regla que dictamina qui en se atiende a continuacion se denomina pol tica de atencion . La m as sencilla se denomina PEPS, por la frase Primero que Entra Primero que Sale. La pol tica m as general es la que implementa una cola de prioridad, en la que cada cliente tiene asignada una prioridad y siempre se atiende el cliente con la prioridad m as alta, sin importar el orden de llegada. Es la pol tica m as general en el sentido de que la prioridad puede asignarse bajo cualquier criterio: la hora de partida de un vuelo, cu antos v veres se van a pagar, o qu e tan importante es el cliente. No todas las pol ticas de atencion son justas, pero la justicia est a denida por el que presta el servicio. El TAD Cola y la cola de prioridad TAD tienen el mismo conjunto de operaciones. La diferencia est a en la sem antica de ellas: una cola utiliza la pol tica PEPS y una cola de prioridad usa la pol tica de prioridad.
Colas
20.3 Desempeno
209
Hay dos invariantes que un objeto Cola bien formado debe cumplir. El valor de numElementos debe ser el numero de nodos en la Cola, y el ultimo nodo debe tener en siguiente el valor None. Verique que este m etodo satisfaga los dos invariantes.
20.3. Desempeno
Usualmente, cuando llamamos un m etodo, no nos interesan los detalles de la implementacion. Sin embargo, hay un detalle que quisi eramos saber el desempeno del nuevo m etodo. Cu anto tarda en ejecutarse y como cambia el tiempo de ejecucion a medida que el numero de objetos en la Cola crece? Primero observemos al m etodo sacar. No hay ciclos ni llamados a funciones, as que el tiempo de ejecucion de este m etodo es el mismo cada vez que se ejecuta. Los m etodos de este tipo se denominan operaciones de tiempo constante. De hecho, el m etodo puede ser mas r apido cuando la lista est a vac a ya que no entra al cuerpo del condicional, pero esta diferencia no es signicativa. El desempeno de meter es muy diferente. En el caso general, tenemos que recorrer la lista para encontrar el ultimo elemento. Este recorrido toma un tiempo proporcional al atributo numElementos de la lista. Ya que el tiempo de ejecucion es una funcion lineal de numElementos, se dice que este m etodo tiene un tiempo de ejecucion de tiempo lineal. Comparado con el tiempo constante, es bastante malo.
carga siguiente
carga siguiente
carga siguiente
None
210 self.numElementos = 0 self.primero = None self.ultimo = None def estaVacia(self): return (self.numElementos == 0)
Colas
Hasta aqu el unico cambio es al nuevo atributo ultimo. Este debe ser usado en los m etodos meter y sacar: class ColaMejorada: ... def meter(self, carga): nodo = nodo(carga) nodo.siguiente = None if self.numElementos == 0: # si est a vac a, el nuevo nodo es primero y ultimo self.primero = self.ultimo = nodo else: # encontrar el ultimo nodo ultimo = self.ultimo # pegar el nuevo nodo ultimo.siguiente = nodo self.ultimo = nodo self.numElementos = self.numElementos + 1 Ya que ultimo lleva la pista del ultimo nodo, no tenemos que buscarlo. Como resultado, este m etodo tiene un tiempo constante de ejecucion. Hay un precio que pagar por esta mejora. Tenemos que agregar un caso especial a sacar que asigne a ultimo el valor None cuando se saca el unico elemento: class ColaMejorada: ... def sacar(self): carga = self.primero.carga self.primero = self.primero.siguiente self.numElementos = self.numElementos - 1 if self.numElementos == 0: self.ultimo = None return carga Esta implementacion es m as compleja que la inicial y es m as dif cil demostrar su correccion. La ventaja es que hemos logrado el objetivo meter y sacar son operaciones que se ejecutan en un tiempo constante.
20.5 Cola de prioridad Como ejercicio, escriba una implementaci on del TAD Cola usando una lista de Python. Compare el desempeno de esta implementaci on con el de la ColaMejorada para un distintos valores de numElementos.
211
212 class ColaPrioridad: ... def sacar(self): maxi = 0 for i in range(1,len(self.items)): if self.items[i] > self.items[maxi]: maxi = i item = self.items[maxi] self.items[maxi:maxi+1] = [] return item
Colas
Al iniciar cada iteracion, maxi almacena el ndice del tem m as grande (con la prioridad m as alta) que hayamos encontrado hasta el momento. En cada iteracion, el programa compara el ie simo tem con el que iba ganando. Si el nuevo es mejor, el valor de maxi se actualiza con el de i. Cuando el for se completa, maxi es el ndice con el mayor tem de todos. Este se saca de la lista y se retorna. Probemos la implementacion: >>> >>> >>> >>> >>> >>> 14 13 12 11 q = ColaPrioridad() q.meter(11) q.meter(12) q.meter(14) q.meter(13) while not q.estaVacia(): print q.sacar()
Si la Cola contiene numeros o cadenas, se sacan en orden alfab etico o num erico, del m as alto al m as bajo. Python puede encontrar el mayor entero o cadena a trav es de los operadores de comparacion primitivos. Si la Cola contiene otro tipo de objeto, creado por el programador, tiene que proporcionar el m etodo cmp . Cuando sacar use al operador > para comparar tems, estar a llamando el m etodo cmp sobre el primero y pas andole al segundo como par ametro. En tanto que cmp funcione correctamente, la cola de prioridad ser a correcta.
20.6 La clase golfista init y str : class golfista: def __init__(self, nombre, puntaje): self.nombre = nombre self.puntaje= puntaje def __str__(self): return "%-16s: %d" % (self.nombre, self.puntaje)
213
str utiliza el operador de formato para poner los nombres y los puntajes en dos columnas. A continuacion denimos una version de cmp en la que el puntaje mas bajo tenga la prioridad mas alta. Recuerde que para Python cmp retorna 1 si self es mayor que otro, -1 si self es menor otro, y 0 si son iguales. class golfista: ... def __cmp__(self, otro): # el menor tiene mayor prioridad if self.puntaje < otro.puntaje: return 1 if self.puntaje > otro.puntaje: return -1 return 0 Ahora estamos listos para probar la cola de prioridad almacenando instancias de la clase golfista: >>> tiger = golfista("Tiger Woods", 61) >>> phil = golfista("Phil Mickelson", 72) >>> hal = golfista("Hal Sutton", 69) >>> >>> pq = ColaPrioridad() >>> pq.meter(tiger) >>> pq.meter(phil) >>> pq.meter(hal) >>> while not pq.estaVacia(): print pq.sacar() Tiger Woods : 61 Hal Sutton : 69 Phil Mickelson : 72 Como ejercicio, escriba una implementaci on del TAD cola de prioridad TAD debe mantenerse ordenada, de forma que sacar usando una lista enlazada. Esta sea una operaci on de tiempo constante. Compare el desempeno de esta implementaci on con la implementaci on basada en listas de Python.
214
Colas
20.7. Glosario
Cola: conjunto ordenado de objetos (o personas) esperando a que se les preste algun servicio Cola: TAD con las operaciones que se realizan en una Cola. Pol tica de atencion: reglas que determinan cu al es el siguiente objeto que se saca (atiende) en una Cola. PEPS: Primero que Entra, Primero que Sale , pol tica de atencion en la que se saca el primer elemento de la Cola. Atencion por prioridad: una pol tica de atencion en la que se saca el elemento de la Cola que tenga la mayor prioridad. Cola de prioridad: un TAD que dene las operaciones que se pueden realizar en una cola de prioridad. Cola enlazada: implementacion de una Cola que utiliza una lista enlazada. Desempeno: toda funcion de un TAD realiza un numero de operaciones b asicas que dependen del numero de elementos que e ste contiene en un momento dado. Por medio de este numero de operaciones b asicas se pueden comparar distintas alternativas de implementacion de una operacion. Tiempo constante: desempeno de una operacion, cuyo tiempo de ejecucion no depende del tamano de la estructura de datos. Tiempo lineal: desempeno de una operacion, cuyo tiempo de ejecucion es una funcion lineal del tamano de la estructura de datos.
Cap tulo 21
Arboles
Como las listas enlazadas, los a rboles est an compuestos de nodos. Una clase muy comun es el a rbol binario, en el que cada nodo contiene referencias a otros dos nodos (posiblemente, valores None). Estas referencias se denominan los sub arboles izquierdo y derecho. Como los nodos de las listas, los nodos de los a rboles tambi en contienen una carga. Un diagrama de estados para los a rboles luce as : arbol
carga izquierdo
1 derecho
carga izquierdo
2 derecho
carga izquierdo
3 derecho
None None None None Para evitar el caos en las guras, a menudo omitimos los valores None. El inicio del a rbol (al nodo al que arbol se reere) se denomina ra z. Para conservar la met afora con los a rboles, los otros nodos se denominan ramas, y los nodos que tienen referencias nulas se llaman hojas. Parece extrano el dibujo con la ra z en la parte superior y las hojas en la inferior, pero esto es solo el principio. Los cient cos de la computacion tambi en usan otra met aforael a rbol genealogi co. El nodo ra z se denomina padre y los nodos a los que se reere hijos, los nodos
216
Arboles
que tienen el mismo padre se denominan hermanos. Finalmente, hay un vocabulario geom etrico para referirse a los a rboles. Ya mencionamos la distincion entre izquierda y derecha, tambi en se acostumbra diferenciar entre arriba (hacia el padre/ra z) y abajo (hacia los hijos/hojas). Adem as, todos los nodos que est an a una misma distancia de la ra z comprenden un nivel. Probablemente no necesitemos estas met aforas para describir los a rboles, pero se usan extensivamente. Como las listas enlazadas, los a rboles son estructuras de datos recursivas ya que su denicion es recursiva. Un a rbol es: el a rbol vac o, representado por None, o Un nodo que contiene una referencia a un objeto y referencias a otros a rboles.
21.2 Recorridos sobre a rboles Podemos escribir esto de una manera m as compacta anidando los llamados:
217
>>> a = arbol(1, arbol(2), arbol(3)) Con las dos formas se obtiene como resultado el a rbol que ilustramos al principio del cap tulo.
def total(a): if a == None: return 0 else: return total(a.izquierdo) + total(a.derecho) + a.carga El caso base se da cuando el argumento es el a rbol vac o, que no tiene carga, as que la suma se dene como 0. El paso recursivo realiza dos llamados recursivos para encontrar la suma de los a rboles hijos, cuando nalizan, se suma a estos valores la carga del padre.
218
arbol
Arboles
carga
izquierdo derecho
carga
carga
izquierdo derecho
izquierdo derecho
carga
carga
izquierdo derecho
izquierdo derecho
Los nodos de un a rbol para una expresion pueden ser operandos como 1 y 2, tambi en operadores como + y *. Los operandos deben ser nodos hoja; y los nodos que contienen operadores tienen referencias a sus operandos. Todos estos operadores son binarios, as que solamente tienen dos operandos. Un a rbol como el siguiente representa la gura anterior: >>> a = arbol(+, arbol(1), arbol(*, arbol(2), arbol(3))) Observando la gura no hay ninguna duda sobre el orden de las operaciones; la multiplicacion ocurre primero para que se calcule el segundo operando de la suma. Los a rboles de expresiones tienen muchos usos. El ejemplo de este cap tulo utiliza a rboles para traducir expresiones entre las notaciones postja, preja, e inja. Arboles similares se usan en los compiladores para analizar sint acticamente, optimizar y traducir programas.
219
En otras palabras, para imprimir un a rbol, primero se imprime el contenido (carga) de la ra z, luego todo el sub arbol izquierdo, y a continuacion todo el sub arbol derecho. Este recorrido se denomina preorden, porque el contenido de la ra z se despliega antes que el contenido de los hijos. Para el ejemplo anterior, la salida es: >>> a = arbol(+, arbol(1), arbol(*, arbol(2), arbol(3))) >>> imprimirarbol(a) + 1 * 2 3 Esta notacion diferente a la inja y a la postja, se denomina preja, porque los operadores aparecen antes que sus operandos. Usted puede sospechar que si se recorre el a rbol de una forma distinta se obtiene otra notacion. Por ejemplo si se despliegan los dos sub arboles primero y a continuacion el nodo ra z, se obtiene def imprimirarbolPostorden(a): if a == None: return else imprimirarbolPostorden(a.izquierdo) imprimirarbolPostorden(a.derecho) print a.carga, El resultado, 1 2 3 * +, est a en notacion postja!. Por esta razon este recorrido se denomina postorden. Finalmente, para recorrer el a rbol en orden, se imprime el a rbol izquierdo, luego la ra z y, por ultimo, el a rbol derecho: def imprimirabolEnOrden(a): if a == None: return imprimirabolEnOrden(a.izquierdo) print a.carga, imprimirabolEnOrden(a.derecho) El resultado es 1 + 2 * 3, la expresion en notacion inja. Por precision debemos anotar que hemos omitido una complicacion importante. Algunas veces cuando escribimos una expresion inja, tenemos que usar par entesis para preservar el orden de las operaciones. As que un recorrido en orden no es suciente en todos los casos para generar una expresion inja.
220
Arboles
Sin embargo, con unas mejoras adicionales, los a rboles de expresiones y los tres recorridos recursivos proporcionan una forma general de traducir expresiones de un formato al otro. Como ejercicio, modique imprimirarbolEnOrden para que despliegue par entesis alrededor de cada operador y pareja de operandos. La salida es correcta e inequ voca? Siempre son necesarios los par entesis? Si realizamos un recorrido en orden y llevamos pista del nivel en el que vamos podemos generar una representacion gr aca del a rbol: def imprimirarbolSangrado(a, nivel=0): if a == None: return imprimirarbolSangrado(a.derecho, nivel+1) print *nivel + str(a.carga) imprimirarbolSangrado(a.izquierdo, nivel+1) El par ametro nivel lleva el nivel actual. Por defecto es 0. Cada vez que hacemos un llamado recursivo pasamos nivel+1, porque el nivel de los hijos siempre es uno m as del nivel del padre. Cada objeto se sangra o indenta con dos espacios por nivel. El resultado para el a rbol de ejemplo es: >>> imprimirarbolSangrado(a) 3 * 2 + 1 Si rota el libro 90 grados en el sentido de las manecillas del reloj ver a una forma simplicada del dibujo al principio del cap tulo.
221
*
+ 3 7 9
Note que hemos simplicado el diagrama ocultando los nombres de los atributos. El an alisis sint actico se har a sobre expresiones que incluyan numeros, par entesis, y los operadores + y *. Asumimos que la cadena de entrada ha sido separada en una lista de lexemas; por ejemplo, para (3+7)*9 la lista de lexemas es: [(, 3, +, 7, ), *, 9, fin] La cadena fin sirve para prevenir que el analizador sint actico siga leyendo m as all a del nal de la lista. Como ejercicio escriba una funci on que reciba una cadena de texto con una expresi on y retorne la lista de lexemas (con la cadena fin al nal). La primera funcion que escribiremos es obtenerLexema, que toma una lista de lexemas y un lexema esperado como par ametros. Compara el lexema esperado con el primero de la lista: si son iguales, elimina el lexema de la lista y retorna True, si no son iguales, retorna False: def obtenerLexema(listaLexemas, esperado): if listaLexemas[0] == esperado: del listaLexemas[0] return 1 else: return 0 Como listaLexemas se reere a un objeto mutable, los cambios que hacemos son visibles en cualquier otra parte del programa que tenga una referencia a la lista. La siguiente funcion, obtenerNumero, acepta operandos. Si el siguiente lexema en listaLexemas es un numero, obtenerNumero lo elimina y retorna un nodo hoja cuya carga ser a el numero; si no es un numero retorna None. def obtenerNumero(listaLexemas): x = listaLexemas[0] if type(x) != type(0):
222 return None del listaLexemas[0] return arbol (x, None, None)
Arboles
Probemos a obtenerNumero con una lista pequena de numeros. Despu es del llamado, imprimimos el a rbol resultante y lo que queda de la lista: >>> listaLexemas = [9, 11, fin] >>> x = obtenerNumero(listaLexemas) >>> imprimirarbolPostorden(x) 9 >>> print listaLexemas [11, fin] El siguiente m etodo que necesitamos es obtenerProducto, que construye un a rbol de expresion para productos. Un producto sencillo tiene dos numeros como operandos, como en 3 * 7. def obtenerProducto(listaLexemas): a = obtenerNumero(listaLexemas) if obtenerLexema(listaLexemas, *): b = obtenerNumero(listaLexemas) return arbol (*, a, b) else: return a Asumiendo que obtenerNumero retorna un a rbol, le asignamos el primer operando a a. Si el siguiente car acter es *, obtenemos el segundo numero y construimos un a rbol de expresion con a, b, y el operador. Si el siguiente car acter es cualquier otro, retornamos el nodo hoja con a. Aqu hay dos ejemplos: >>> listaLexemas = [9, *, 11, fin] >>> a = obtenerProducto(listaLexemas) >>> imprimirarbolPostorden(a) 9 11 *
>>> listaLexemas = [9, +, 11, fin] >>> arbol = obtenerProducto(listaLexemas) >>> imprimirarbolPostorden(arbol) 9
223
El segundo ejemplo implica que consideramos que un solo operando sea tratado como una clase de producto. Esta denicion de producto es contraintuitiva, pero resulta ser muy provechosa. Ahora, tenemos que manejar los productos compuestos, como 3 * 5 * 13. Esta expresion es un producto de productos, vista as : 3 * (5 * 13). El a rbol resultante es:
*
3 5
*
13
Con un pequeno cambio en obtenerProducto, podemos analizar un producto arbitrariamente largo: def obtenerProducto(listaLexemas): a = obtenerNumero(listaLexemas) if obtenerLexema(listaLexemas, *): b = obtenerProducto(listaLexemas) return arbol (*, a, b) else: return a
En otras palabras, un producto puede ser un a rbol singular o un a rbol con * en la ra z, un numero en el sub arbol izquierdo, y un producto en el sub arbol derecho. Esta clase de denicion recursiva deber a empezar a ser familiar. Probemos la nueva version con un producto compuesto: >>> >>> >>> 2 3 listaLexemas = [2, *, 3, *, 5 , *, 7, fin] a = obtenerProducto(listaLexemas) imprimirarbolPostorden(arbol) 5 7 * * *
Ahora, agregaremos la posibilidad de analizar sumas. Otra vez daremos una denicion contraintuitiva de la suma. Una suma puede ser un a rbol con + en la ra z, un producto en el sub arbol izquierdo y una suma en el sub arbol derecho. O, una suma puede ser solo un producto. Si usted analiza esta denicion encontrar a que tiene una propiedad muy bonita: podemos representar cualquier expresion (sin par entesis) como una suma de productos. Esta propiedad es el fundamento de nuestro algoritmo de an alisis sint actico.
224
Arboles
obtenerSuma intenta construir un a rbol con un producto en izquierdo y una suma en derecho. Pero si no encuentra un +, solamente construye un producto. def obtenerSuma(listaLexemas): a = obtenerProducto(listaLexemas) if obtenerLexema(listaLexemas, +): b = obtenerSuma(listaLexemas) return arbol (+, a, b) else: return a Probemos con 9 * 11 + 5 * 7: >>> listaLexemas = [9, *, 11, +, 5, *, 7, fin] >>> a = obtenerSuma(listaLexemas) >>> imprimirarbolPostorden( arbol) 9 11 * 5 7 * + Casi terminamos, pero todav a faltan los par entesis. En cualquier posicion de una expresion donde podamos encontrar un numero puede tambi en haber una suma completa cerrada entre par entesis. Necesitamos modicar obtenerNumero para que sea capaz de manejar subexpresiones: def obtenerNumero(listaLexemas): if obtenerLexema(listaLexemas, (): # obtiene la subexpresi on x = obtenerSuma(listaLexemas) # elimina los par entesis obtenerLexema(listaLexemas, )) return x else: x = listaLexemas[0] if type(x) != type(0): return None listaLexemas[0:1] = [] return arbol (x, None, None) Probemos esto con 9 * (11 + 5) * 7: >>> listaLexemas = [9, *, (, 11, +, 5, ),*, 7, fin] >>> arbol = obtenerSuma(listaLexemas) >>> imprimirarbolPostorden(arbol) 9 11 5 + 7 * *
225
El analizador manejo los par entesis correctamente, la suma se hace antes que la multiplicacion. En la version nal del programa, ser a bueno nombrar a obtenerNumero con un rol m as descriptivo.
226 Esta pensando en un animal? s ajaro? n Es un p Cual es el nombre del animal? perro Que pregunta permite distinguir entre un perro y un p ajaro? Puede volar Si el animal fuera perro la respuesta ser a? n Esta pensando en un animal? s Puede volar? n Es un perro? n Cual es el nombre del animal? gato Que pregunta permite distinguir un gato de un perro? Ladra Si el animal fuera un gato a? n la respuesta ser Esta pensando en un animal? y Puede volar? n Ladra? s Es un perro? s Soy el mejor! Este es el a rbol que el di alogo genera:
Arboles
Al principio de cada ronda, el programa empieza en la ra z del a rbol y hace la primera pregunta. Dependiendo de la respuesta se mueve al sub arbol izquierdo o derecho y continua hasta que llega a un nodo hoja. En ese momento conjetura. Si falla, le pregunta al usuario el nombre del animal y una pregunta que le permitir a distinguir el animal conjeturado del real. Con esta informacion agrega un nodo al a rbol con la nueva pregunta y el nuevo animal. Aqu est a el codigo fuente:
21.7 El a rbol de animales def animal(): # Un solo nodo raiz = arbol("pajaro") # Hasta que el usuario salga while True: print if not si("Esta pensando en un animal? "): break # Recorrer el arbol arbol = raiz while arbol.obtenerizquierdo() != None: pregunta = arbol.obtenercarga() + "? " if si(pregunta): arbol = arbol.obtenerderecho() else: arbol = arbol.obtenerizquierdo() # conjetura conjetura = arbol.obtenercarga() pregunta = "Es un" + conjetura + "? " if si(pregunta): print "Soy el mejor!" continue # obtener pregunta animal = pregunta mas informacion = "Cual es el nombre el animal? " raw_input(pregunta) = "Que pregunta permitiria distinguir un %s de un %s? " q = raw_input(pregunta % (animal,conjetura)) # agrega un nuevo nodo arbol arbol.asignarcarga(q) pregunta = "Si el animal fuera %s la respuesta ser a? " if si(pregunta % animal): arbol.asignarizquierdo(arbol(conjetura)) arbol.asignarderecho(arbol(animal)) else: arbol.asignarizquierdo(arbol(animal))
227
228 arbol.asignarderecho(arbol(conjetura))
Arboles
La funcion si es auxiliar, imprime una pregunta y recibe la respuesta del usuario. Si la respuesta empieza con s o S, retorna cierto: def si(preg): from string import lower r = lower(raw_input(preg)) return (r[0] == s) La condicion del ciclo es True, lo que implica que el ciclo iterar a hasta que la sentencia break se ejecute cuando el usuario deje de pensar en animales. El ciclo while interno recorre el a rbol desde la ra z hasta el fondo, gui andose por las respuestas del usuario. Cuando se agrega un nuevo nodo al a rbol, la nueva pregunta reemplaza la carga, y los dos hijos son el animal nuevo y la carga original. Una limitacion seria de este programa es que cuando naliza, olvida todo lo que se le ha ensenado! Como ejercicio, piense en diferentes maneras de guardar este a rbol de conocimiento en un archivo. Implemente la que parezca m as sencilla.
21.8. Glosario
Arbol binario: un a rbol en el que cada nodo se reere a cero, uno o dos nodos, llamados hijos. Ra z: nodo inicial en un a rbol, es el unico que no tiene padre. Hoja: nodo que no tiene hijos y se encuentra lo mas abajo posible. Padre: nodo que tiene la referencia hacia otro nodo. Hijo: uno de los nodos referenciados por un nodo. Hermanos: nodos que comparten un padre comun. Nivel: conjunto de nodos equidistantes a la ra z. Operador binario: operador que acepta dos operandos. Subexpresion: expresion en par entesis que se puede ver como un solo operando en una expresion m as grande. Preorden: es el recorrido sobre un a rbol en el que se visita cada nodo antes que a sus hijos.
21.8 Glosario
229
Notacion preja: notacion para escribir una expresion matem atica en la que cada operador aparece antes de sus operandos. Postorden: forma de recorrer un a rbol, visitando los hijos de un nodo antes que a este. En orden: forma de recorrer un a rbol, visitando primero el sub arbol izquierdo, luego la ra z y por ultimo, el sub arbol derecho.
Ap endice A
Depuracion
Hay diferentes tipos de error que pueden suceder en un programa y es muy util distinguirlos a n de rastrearlos m as r apidamente: Los errores sint acticos se producen cuando Python traduce el codigo fuente en codigo objeto. Usualmente indican que hay algun problema en la sintaxis del programa. Por ejemplo, omitir los puntos seguidos al nal de una sentencia def produce un mensaje de error un poco redundante SyntaxError: invalid syntax. Los errores en tiempo de ejecucion se producen por el sistema de ejecucion, si algo va mal mientras el programa corre o se ejecuta. La mayor a de errores en tiempo de ejecucion incluyen informacion sobre la localizacion del error y las funciones que se estaban ejecutando. Ejemplo: una recursion innita eventualmente causa un error en tiempo de ejecucion de maximum recursion depth exceeded. Los errores sem anticos se dan en programas que compilan y se ejecutan normalmente, pero no hacen lo que se pretend a. Ejemplo: una expresion podr a evaluarse en un orden inesperado, produciendo un resultado incorrecto. El primer paso en la depuracion consiste en determinar la clase de error con la que se est a tratando. Aunque las siguientes secciones se organizan por tipo de error, algunas t ecnicas se aplican en m as de una situacion.
A.1.
Los errores sint acticos se corrigen f acilmente una vez que usted ha determinado a qu e apuntan. Desafortunadamente, en algunas ocasiones los mensajes de error
232
Depuracion
no son de mucha ayuda. Los mensajes de error m as comunes son SyntaxError: invalid syntax y SyntaxError: invalid token, que no son muy informativos. Por otro lado, el mensaje s dice donde ocurre el problema en el programa. M as precisamente, dice donde fue que Python encontro un problema, que no necesariamente es el lugar donde est a el error. Algunas veces el error est a antes de la localizacion que da el mensaje de error, a menudo en la l nea anterior. Si usted est a construyendo los programas incrementalmente, deber a tener una buena idea de donde se localiza el error. Estar a en la ultima l nea que se agrego. Si usted est a copiando codigo desde un libro, comience por comparar su codigo y el del libro muy cuidadosamente. Chequee cada car acter. Al mismo tiempo, recuerde que el libro puede tener errores, as que si encuentra algo que parece un error sint actico, entonces debe serlo. Aqu hay algunas formas de evitar los errores sint acticos m as comunes: 1. Asegurese de no usar una palabra reservada de Python como nombre de variable 2. Chequee que haya colocado dos puntos seguidos al nal de la cabecera de cada sentencia compuesta, incluyendo los ciclos for, while, los condicionales if, las deniciones de funcion def y las clases. 3. Chequee que la indentacion o sangrado sea consistente. Se puede indentar con espacios o tabuladores, pero es mejor no mezclarlos. Cada nivel debe sangrarse la misma cantidad de espacios o tabuladores. 4. Asegurese de que las cadenas en los programas est en encerradas entre comillas. 5. Si usted tiene cadenas multil nea creadas con tres comillas consecutivas, verique su terminacion. Una cadena no terminada puede causar un error denominado invalid token al nal de su programa, o puede tratar la siguiente parte del programa como si fuera una cadena, hasta toparse con la siguiente cadena. En el segundo caso, puede que Python no produzca ningun mensaje de error! 6. Un par entesis sin cerrar(, {, o [hace que Python continue con la siguiente l nea como si fuera parte de la sentencia actual. Generalmente, esto causa un error inmediato en la siguiente l nea. 7. Busque por la confusion cl asica entre = y ==, adentro y afuera de los condicionales. Si nada de esto funciona, avance a la siguiente seccion...
233
A.2.
Cuando su programa est a bien sint acticamente, Python puede importarlo y empezar a ejecutarlo. Qu e podr a ir mal ahora?
234
Depuracion mum recursion depth exceeded. Si esto ocurre revise la seccion posterior Recursion Innita. Si no est a obteniendo este error, pero sospecha que hay un problema con una funcion recursiva o m etodo, tambi en puede usar las t ecnicas de la seccion Recursion Innita. Si ninguno de estos pasos funciona, revise otros ciclos y otras funciones recursivas, o m etodos. Si eso no funciona entonces es posible que usted no comprenda el ujo de ejecucion que hay en su programa. Vaya a la seccion posterior Flujo de ejecucion.
Ciclo innito Si usted cree que tiene un ciclo innito anada una sentencia print al nal de e ste que imprima los valores de las variables de ciclo (las que aparecen en la condicion) y el valor de la condicion. Por ejemplo: while x > 0 and y < 0 : # hace algo con x # hace algo con y print print print "x: ", x "y: ", y "condicion: ", (x > 0 and y < 0)
Ahora, cuando ejecute el programa, usted ver a tres l neas de salida para cada iteracion del ciclo. En la ultima iteracion la condicion debe ser falsa. Si el ciclo sigue, usted podr a ver los valores de x y y, y puede deducir por qu e no se est an actualizando correctamente. Recursion innita La mayor a de las veces una recursion innita causar a que el programa se ejecute durante un momento y luego producir a un error: Maximum recursion depth exceeded. Si sospecha que una funcion o m etodo est a causando una recursion innita, empiece por chequear la existencia de un caso base. En otras palabras, debe haber una condicion que haga que el programa o m etodo retorne sin hacer un llamado recursivo. Si no lo hay, es necesario reconsiderar el algoritmo e identicar un caso base.
235
Si hay un caso base, pero el programa no parece alcanzarlo, anada una sentencia print al principio de la funcion o m etodo que imprima los par ametros. Ahora, cuando ejecute el programa usted ver a unas pocas l neas de salida cada vez que la funcion o m etodo es llamada, y podr a ver los par ametros. Si no est an cambiando de valor acerc andose al caso base, usted podr a deducir por qu e ocurre esto. Flujo de ejecucion Si no est a seguro de como se mueve el ujo de ejecucion a trav es de su programa, anada sentencias print al comienzo de cada funcion con un mensaje como entrando a la funcion foo, donde foo es el nombre de la funcion. Ahora, cuando ejecute el programa, se imprimir a una traza de cada funcion a medida que van siendo llamadas.
236
Depuracion que se hace el llamado sobre un objeto del tipo correcto y de pasar los otros par ametros correctamente.
KeyError: usted est a tratando de acceder a un elemento de un diccionario usando una llave que e ste no contiene. AttributeError: est a tratando de acceder a un atributo o m etodo que no existe. IndexError: el ndice que est a usando para acceder a una lista, cadena o tupla es m as grande que su longitud menos uno. Inmediatamente, antes de la l nea del error, agregue una sentencia print para desplegar el valor del ndice y la longitud del arreglo. Tiene e ste el tamano correcto? Tiene el ndice el valor correcto?
A.2.4. Agregu e tantas sentencias print que estoy inundado de texto de salida
Uno de los problemas de usar sentencias print para depurar es que uno puede terminar inundado de salida. Hay dos formas de proceder: simplicar la salida o simplicar el programa. Para simplicar la salida se pueden eliminar o comentar sentencias print que no son de ayuda, o se pueden combinar, o se puede dar formato a la salida, de forma que quede m as f acil de entender. Para simplicar el programa hay varias cosas que se pueden hacer. Primero, disminuya la escala del problema que el programa intenta resolver. Por ejemplo, si usted est a ordenando un arreglo, utilice uno pequeno como entrada. Si el programa toma entrada del usuario, p asele la entrada m as simple que causa el problema. Segundo, limpie el programa. Borre el codigo muerto y reorgan zelo para hacerlo lo m as legible que sea posible. Por ejemplo, si usted sospecha que el problema est a en una seccion de codigo profundamente anidada, intente reescribir esa parte con una estructura m as sencilla. Si sospecha de una funcion grande, trate de partirla en funciones mas pequenas y pru ebelas separadamente. Este proceso de encontrar el caso m nimo de prueba que activa el problema a menudo permite encontrar el error. Si usted encuentra que el programa funciona en una situacion, pero no en otras, esto le da una pista de lo que est a sucediendo. Similarmente, reescribir un trozo de codigo puede ayudar a encontrar errores muy sutiles. Si usted hace un cambio que no deber a alterar el comportamiento del programa, y s lo hace, esto es una senal de alerta.
237
A.3.
Estos son los mas dif ciles de depurar porque ni el compilador ni el sistema de ejecucion dan informacion sobre lo que est a fallando. Solo usted sabe lo que el programa debe hacer y solo usted sabe por qu e no lo est a haciendo bien. El primer paso consiste en hacer una conexion entre el codigo fuente del programa y el comportamiento que est a d andose. Ahora, usted necesita una hipotesis sobre lo que el programa est a haciendo realmente. Una de las cosas que complica el asunto es que la ejecucion de programas en un computador moderno es muy r apida. A veces desear a desacelerar el programa hasta una velocidad humana, y con algunos programas depuradores esto es posible. Pero el tiempo que toma insertar unas sentencias print bien situadas a menudo es mucho m as corto comparado con la conguracion del depurador, la insercion y eliminacion de puntos de quiebre (breakpoints en ingl es) y saltar por el programa al punto donde el error se da.
238
Depuracion
A.3.2. He obtenido una expresion grande y peluda que no hace lo que espero
Escribir expresiones complejas est a bien en tanto queden legibles, sin embargo, puede ser dif cil depurarlas. Es una buena idea separar una expresion compleja en una serie de asignaciones a variables temporales. Por ejemplo: self.manos[i].agregarCarta( self.manos[self.encontrarVecino(i)].eliminarCarta()) Esto puede reescribirse como: vecino = self.encontrarVecino (i) cartaEscogida = self.manos[vecino].eliminarCarta() self.manos[i].agregarCarta(cartaEscogida) La version expl cita es m as f acil de leer porque los nombres de variables proporcionan una documentacion adicional y porque se pueden chequear los tipos de los valores intermedios despleg andolos. Otro problema que ocurre con las expresiones grandes es que el orden de evaluacion puede no ser el que usted espera. Por ejemplo, si usted est a traduciendo la a Python, podr a escribir: expresion 2x y = x / 2 * math.pi; Esto es incorrecto, porque la multiplicacion y la division tienen la misma precedencia y se evaluan de izquierda a derecha. As que ese codigo calcula x/2. Una buena forma de depurar expresiones es agregar par entesis para hacer expl cito el orden de evaluacion: y = x / (2 * math.pi); Cuando no est e seguro del orden de evaluacion, use par entesis. No solo corregir a el programa si hab a un error, sino que tambi en lo har a mas legible para otras personas que no se sepan las reglas de precedencia.
A.3 Errores sem anticos se podr a escribir: cont = self.manos[i].eliminarParejas() return cont
239
240
Depuracion Cu al fue el ultimo cambio antes de que se presentara el error? Cu ales fueron las ultimas l neas de codigo que escribio usted o cu al es el nuevo caso de prueba que falla? Qu e ha intentado hasta ahora, y qu e ha aprendido sobre el programa?
Cuando encuentre el error, tomese un segundo para pensar sobre lo que podr a haber realizado para encontrarlo m as r apido. La proxima vez que le ocurra algo similar, ser a capaz de encontrar el error r apidamente. Recuerde, el objetivo no solo es hacer que el programa funcione. El objetivo es aprender como hacer que los programas funcionen.
Ap endice B
242
Para realizar pruebas, ponemos este codigo en un archivo Fraccion.py y lo importamos en el int erprete de Python. Ahora creamos un objeto fraccion y lo imprimimos. >>> from Fraccion import Fraccion >>> s = Fraccion(5,6) >>> print "La fraccion es", s La fraccion es 5/6 El m etodo print, autom aticamente invoca al m etodo str de manera impl cita.
243
Ahora, la multiplicacion entre enteros y fracciones funciona, pero solo si la fraccion es el operando a la izquierda : >>> print Fraccion(5,6) * 4 20/6 >>> print 4 * Fraccion(5,6) TypeError: __mul__ nor __rmul__ defined for these operands Para evaluar un operador binario como la multiplicacion, Python chequea el operando izquierdo primero, para ver si su clase dene el m etodo mul , y que tenga soporte para el tipo del segundo operando. En este caso el operador primitivo para multiplicar enteros no soporta las fracciones. Despu es, Python chequea si el operando a la derecha provee un m etodo rmul que soporte el tipo del operando de la izquierda. En este caso, como no hay denicion de rmul en la clase Fraccion, se genera un error de tipo. Hay una forma sencilla de denir rmul : class Fraccion: ... __rmul__ = __mul__ que mul . Si ahora Esta asignacion dice que rmul contiene el mismo codigo evaluamos 4 * Fraccion(5,6), Python llama a rmul y le pasa al 4 como par ametro: >>> print 4 * Fraccion(5,6) 20/6 Como rmul tiene el mismo codigo que mul , y el m etodo mul puede recibir un par ametro entero, nuestra multiplicacion de fracciones funciona bien.
* otro.denominador +
244
__radd__ = __add__ Podemos probar estos m etodos con objetos Fraccion y con numeros enteros. >>> print Fraccion(5,6) + Fraccion(5,6) 60/36 >>> print Fraccion(5,6) + 3 23/6 >>> print 2 + Fraccion(5,6) 17/6 Los primeros ejemplos llaman al m etodo add ; el ultimo ejemplo llama al m etodo radd .
245
Como todas las operaciones que hemos escrito crean nuevas fracciones como resultado, podemos simplicar todos los valores de retorno modicando el m etodo constructor. class Fraccion: def __init__(self, numerador, denominador=1): g = MDC (numerador, denominador) self.numerador = numerador / g self.denominador = denominador / g Ahora, cada vez que creamos una nueva Fraccion, se simplica!. >>> Fraccion(100,-36) -25/9 Una caracter stica adicional que nos provee MDC es que si la fraccion es negativa, el signo menos siempre se mueve hacia el numerador.
246
B.6. Glosario
M aximo divisor comun (MDC): el entero positivo m as grande que divide exactamente a dos numeros (por ejemplo, el numerador y el denominador en una fraccion). Simplicar: cambiar una fraccion en otra equivalente que tenga un MDC de 1. negacion unaria: la operacion que calcula un inverso aditivo, usualmente representada con un signo menos. Es unaria en contraposicion con el menos binario que representa a la resta.
Ap endice C
Programas completos
C.1. Clase punto
class Punto: def __init__(self, x=0, y=0): self.x = x self.y = y def __str__(self): return ( + str(self.x) + , + str(self.y) + ) def __add__(self, otro): return Punto(self.x + otro.x, self.y + otro.y) def __sub__(self, otro): return Punto(self.x - otro.x, self.y - otro.y) def __mul__(self, otro): return self.x * otro.x + self.y * otro.y def __rmul__(self, otro): return Punto(otro * self.x, otro * self.y) def invertir(self): self.x, self.y = self.y, self.x def DerechoYAlReves(derecho): from copy import copy
Programas completos
C.2.
Clase hora
class Hora: def __init__(self, hora=0, minutos=0, segundos=0): self.hora = hora self.minutos = minutos self.segundos = segundos def __str__(self): return str(self.hora) + ":" + str(self.minutos) + ":" + str(self.segundos) def convertirAsegundos(self): minutos = self.hora * 60 + self.minutos segundos = self.minutos * 60 + self.segundos return segundos def incrementar(self, segs): segs = segs + self.segundos self.hora = self.hora + segs/3600 segs = segs % 3600 self.minutos = self.minutos + segs/60 segs = segs % 60 self.segundos = segs def crearHora(segs): H = Hora() H.hora = segs/3600 segs = segs - H.hora * 3600 H.minutos = segs/60 segs = segs - H.minutos * 60 H.segundos = segs return H
249
C.3.
import random class Carta: listaFiguras = ["Treboles", "Diamantes", "Corazones", "Picas"] listaValores = [ "narf", "As", "2", "3", "4", "5", "6", "7","8", "9", "10","Jota", "Reina", "Rey"] def __init__(self, figura=0, valor=0): self.figura = figura self.valor = valor def __str__(self): return self.listaValores[self.valor] + " de " + self.listaFiguras[self.figura] def __cmp__(self, otro): # revisa las figuras if self.figura > otro.figura: return 1 if self.figura < otro.figura: return -1 # las figuras son iguales, se chequean los valores if self.valor > otro.valor: return 1 if self.valor < otro.valor: return -1 # los valores son iguales, hay empate return 0 class Mazo: def __init__(self): self.Cartas = [] for figura in range(4): for valor in range(1, 14): self.Cartas.append(Carta(figura, valor)) def imprimirMazo(self): for Carta in self.Cartas: print Carta
250
Programas completos
def __str__(self): s = "" for i in range(len(self.Cartas)): s = s + " "*i + str(self.Cartas[i]) + "\n" return s def barajar(self): import random nCartas = len(self.Cartas) for i in range(nCartas): j = random.randrange(i, nCartas) [self.Cartas[i], self.Cartas[j]] = [self.Cartas[j], self.Cartas[i]] def eliminarCarta(self, Carta): if Carta in self.Cartas: self.Cartas.remove(Carta) return 1 else: return 0 def entregarCarta(self): return self.Cartas.pop() def estaVacio(self): return (len(self.Cartas) == 0) def repartir(self, manos, nCartas=999): nmanos = len(manos) for i in range(nCartas): if self.estaVacio(): break # rompe el ciclo si no hay cartas # quita la carta del tope Carta = self.entregarCarta() # quien tiene el proximo turnoo? mano = manos[i % nmanos] # agrega la carta a la mano mano.agregarCarta(Carta) class mano(Mazo): def __init__(self, nombre=""): self.Cartas = []
C.3 Cartas, mazos y juegos self.nombre = nombre def agregarCarta(self,Carta) : self.Cartas.append(Carta) def __str__(self): s = "mano " + self.nombre if self.estaVacio(): s = s + " esta vacia\n" else: s = s + " contiene\n" return s + Mazo.__str__(self) class JuegoCartas: def __init__(self): self.Mazo = Mazo() self.Mazo.barajar() class ManoJuegoSolterona(mano): def eliminarParejas(self): cont = 0 originalCartas = self.Cartas[:] for carta in originalCartas: m = Carta(3-carta.figura, carta.valor) if m in self.Cartas: self.Cartas.remove(carta) self.Cartas.remove(m) print "mano %s: %s parejas %s" % (self.nombre,carta,m) cont = cont+1 return cont class JuegoSolterona(JuegoCartas): def jugar(self, nombres): # elimina la reina de treboles self.Mazo.eliminarCarta(Carta(0,12)) # crea manos con base en los nombres self.manos = [] for nombre in nombres : self.manos.append(ManoJuegoSolterona(nombre))
251
252
Programas completos # reparte las Cartas self.Mazo.repartir(self.manos) print "---------- Cartas se han repartido" self.imprimirmanos() # eliminar parejas iniciales parejas = self.eliminarParejas() print "----- parejas descartadas, empieza el juego" self.imprimirmanos() # jugar hasta que se eliminan 50 cartas turno = 0 nummanos = len(self.manos) while parejas < 25: parejas = parejas + self.jugarUnturno(turno) turno = (turno + 1) % nummanos print "---------- Juego Terminado" self.imprimirmanos ()
def eliminarParejas(self): cont = 0 for mano in self.manos: cont = cont + mano.eliminarParejas() return cont def jugarUnturno(self, i): if self.manos[i].estaVacio(): return 0 vecino = self.encontrarvecino(i) cartaEscogida = self.manos[vecino].entregarCarta() self.manos[i].agregarCarta(cartaEscogida) print "mano", self.manos[i].nombre, "escogi o", cartaEscogida count = self.manos[i].eliminarParejas() self.manos[i].barajar() return count def encontrarvecino(self, i): nummanos = len(self.manos) for siguiente in range(1,nummanos): vecino = (i + siguiente) % nummanos
C.4 Listas enlazadas if not self.manos[vecino].estaVacio(): return vecino def imprimirmanos(self) : for mano in self.manos : print mano
253
C.4.
Listas enlazadas
def imprimirlista(Nodo) : while Nodo : print Nodo, Nodo = Nodo.siguiente print def imprimirAlReves(lista) : if lista == None : return cabeza = lista resto = lista.siguiente imprimirAlReves(resto) print cabeza, def imprimirAlRevesBien(lista) : print "(", if lista != None : cabeza = lista resto = lista.siguiente imprimirAlReves(resto) print cabeza, print ")", def eliminarSegundo(lista) : if lista == None : return first = lista second = lista.siguiente first.siguiente = second.siguiente second.siguiente = None return second
254 class Nodo : def __init__(self, carga=None) : self.carga = carga self.siguiente = None def __str__(self) : return str(self.carga) def imprimirAlReves(self) : if self.siguiente != None : resto = self.siguiente resto.imprimirAlReves() print self.carga, class ListaEnlazada : def __init__(self) : self.numElementos = 0 self.cabeza = None def imprimirAlReves(self) : print "(", if self.cabeza != None : self.cabeza.imprimirAlReves() print ")",
Programas completos
def agregarAlPrincipio(self, carga) : nodo = Nodo(carga) nodo.siguiente = self.cabeza self.cabeza = nodo self.numElementos = self.numElementos + 1
C.5.
Clase pila
class Pila: def __init__(self): self.items = [] def meter(self, item): self.items.append(item) def sacar(self):
C.6 Colas PEPS y de colas de prioridad return self.items.pop() def estaVacia(self): return(self.items == []) def evalPostfija(expr) : import re expr = re.split("([0-9])", expr) pila = Pila() for lexema in expr : if lexema == or lexema == : continue if lexema == + : suma = pila.sacar() + pila.sacar() pila.meter(suma) elif lexema == * : producto = pila.sacar() * pila.sacar() pila.meter(producto) else : pila.meter(int(lexema)) return pila.sacar()
255
C.6.
class Cola: def __init__(self): self.numElementos = 0 self.primero = None def estaVacia(self): return (self.numElementos == 0) def meter(self, carga): nodo = Nodo(carga) nodo.siguiente = None if self.primero == None: # si esta vacia este nodo sera el primero self.primero = nodo else: # encontrar el ultimo nodo
256
Programas completos
ultimo = self.primero while ultimo.siguiente: ultimo = ultimo.siguiente # pegar el nuevo ultimo.siguiente = nodo self.numElementos = self.numElementos + 1 def sacar(self): carga = self.primero.carga self.primero = self.primero.siguiente self.numElementos = self.numElementos - 1 return carga class ColaMejorada: def __init__(self): self.numElementos = 0 self.primero = None self.ultimo = None def estaVacia(self): return (self.numElementos == 0) def meter(self, carga): nodo = Nodo(carga) nodo.siguiente = None if self.numElementos == 0: # si esta vacia, el nuevo nodo # es primero y ultimo self.primero = self.ultimo = nodo else: # encontrar el ultimo nodo ultimo = self.ultimo # pegar el nuevo nodo ultimo.siguiente = nodo self.ultimo = nodo self.numElementos = self.numElementos + 1 def sacar(self): carga = self.primero.carga self.primero = self.primero.siguiente self.numElementos = self.numElementos - 1 if self.numElementos == 0:
C.6 Colas PEPS y de colas de prioridad self.ultimo = None return carga class ColaPrioridad: def __init__(self): self.items = [] def estaVacia(self): return self.items == [] def meter(self, item): self.items.append(item) def eliminar(self) : maxi = 0 for i in range(1,len(self.items)) : if self.items[i] > self.items[maxi] : maxi = i item = self.items[maxi] self.items[maxi:maxi+1] = [] return item def sacar(self): maxi = 0 for i in range(0,len(self.items)): if self.items[i] > self.items[maxi]: maxi = i item = self.items[maxi] self.items[maxi:maxi+1] = [] return item
257
class golfista: def __init__(self, nombre, puntaje): self.nombre = nombre self.puntaje= puntaje def __str__(self): return "%-16s: %d" % (self.nombre, self.puntaje) def __cmp__(self, otro): # el menor tiene mayor prioridad
258
Programas completos if self.puntaje < otro.puntaje: return 1 if self.puntaje > otro.puntaje: return -1 return 0
C.7.
Arboles
class Arbol: def __init__(self, carga, izquierdo=None, derecho=None): self.carga = carga self.izquierdo = izquierdo self.derecho = derecho def __str__(self): return str(self.carga) def obtenerizquierdo(self): return self.izquierdo def obtenerderecho(self): return self.derecho def obtenercarga(self): return self.carga def asignarcarga(self, carga): self.carga = carga def asignarizquierdo(self, i): self.izquierdo = i def asignarderecho(self, d): self.derecho = d def total(arbol): if arbol.izquierdo == None or arbol.derecho== None: return arbol.carga else: return total(arbol.izquierdo) + total(arbol.derecho) + arbol.carga
C.8 Arboles de expresiones def imprimirarbol(arbol): if arbol == None: return else: print arbol.carga imprimirarbol(arbol.izquierdo) imprimirarbol(arbol.derecho) def imprimirarbolPostorden(arbol): if arbol == None: return else: imprimirarbolPostorden(arbol.izquierdo) imprimirarbolPostorden(arbol.derecho) print arbol.carga def imprimirabolEnOrden(arbol): if arbol == None: return imprimirabolEnOrden(arbol.izquierdo) print arbol.carga,imprimirabolEnOrden(arbol.derecho) def imprimirarbolSangrado(arbol, nivel=0): if arbol == None: return imprimirarbolSangrado(arbol.derecho, nivel+1) print " "*nivel + str(arbol.carga) imprimirarbolSangrado(arbol.izquierdo, nivel+1)
259
C.8.
Arboles de expresiones
def obtenerLexema(listaLexemas, esperado): if listaLexemas[0] == esperado: del listaLexemas[0] return 1 else: return 0 def obtenerNumero(listaLexemas): x = listaLexemas[0] if type(x) != type(0):
260 return None del listaLexemas[0] return arbol (x, None, None)
Programas completos
def obtenerProducto(listaLexemas): a = obtenerNumero(listaLexemas) if obtenerLexema(listaLexemas, "*"): b = obtenerProducto(listaLexemas) return arbol ("*", a, b) else: return a def obtenerNumero(listaLexemas): if obtenerLexema(listaLexemas, "("): # obtiene la subexpresi on x = obtenerSuma(listaLexemas) # elimina los par entesis obtenerLexema(listaLexemas, ")") return x else: x = listaLexemas[0] if type(x) != type(0): return None listaLexemas[0:1] = [] return Arbol (x, None, None)
C.9.
Adivinar el animal
def animal(): # Un solo nodo raiz = Arbol("pajaro") # Hasta que el usuario salga while True: print if not si("Esta pensando en un animal? "): break # Recorrer el arbol arbol = raiz while arbol.obtenerizquierdo() != None:
C.10 Clase Fraccion pregunta = arbol.obtenercarga() + "? " if si(pregunta): arbol = arbol.obtenerderecho() else: arbol = arbol.obtenerizquierdo() # conjetura conjetura = arbol.obtenercarga() pregunta = "Es un" + conjetura + "? " if si(pregunta): print "Soy el mejor!" continue # obtener mas informacion pregunta = "Cual es el nombre el animal? " animal = raw_input(pregunta) pregunta = "Que pregunta permitiria distinguir un %s de un %s? " q = raw_input(pregunta % (animal,conjetura)) # agrega un nuevo nodo arbol arbol.asignarcarga(q) pregunta = "Si el animal fuera %s la respuesta ser a? " if si(pregunta % animal): arbol.asignarizquierdo(Arbol(conjetura)) arbol.asignarderecho(Arbol(animal)) else: arbol.asignarizquierdo(Arbol(animal)) arbol.asignarderecho(Arbol(conjetura)) def si(preg): from string import lower r = lower(raw_input(preg)) return (r[0] == s)
261
C.10.
Clase Fraccion
262
Programas completos
def __str__(self): return "%d/%d" % (self.numerador, self.denominador) def __mul__(self, otro): if type(otro) == type(5): otro = Fraccion(otro) return Fraccion(self.numerador * otro.numerador, self.denominador * otro.denominador) __rmul__ = __mul__ #suma de fracciones def __add__(self, otro): if type(otro) == type(5): otro = Fraccion(otro) return Fraccion(self.numerador * otro.denominador + self.denominador * otro.numerador, self.denominador * otro.denominador) __radd__ = __add__ def __init__(self, numerador, denominador=1): g=MDC(numerador,denominador) self.numerador=numerador / g self.denominador=denominador / g def __cmp__(self, otro): dif = (self.numerador * otro.denominador otro.numerador * self.denominador) return dif def MDC (m,n): if m%n==0: return n else: return MDC(n,m%n)
Ap endice D
264
Lecturas adicionales recomendadas La programacion en la Web integra a Python con Internet. Por ejemplo, usted puede construir programas cliente que abran y lean p aginas remotas (casi) tan f acilmente como abren un archivo en disco. Tambi en hay modulos en Python que permiten acceder a archivos remotos por medio de ftp, y otros que posibilitan enviar y recibir correo electronico. Python tambi en se usa extensivamente para desarrollar servidores web que presten servicios. Las bases de datos son como superarchivos, donde la informacion se almacena en esquemas predenidos y las relaciones entre los datos permiten navegar por ellos de diferentes formas. Python tiene varios modulos que facilitan conectar programas a varios motores de bases de datos, de codigo abierto y comerciales. La programacion con hilos permite ejecutar diferentes hilos de ejecucion dentro de un mismo programa. Si usted ha tenido la experiencia de desplazarse al inicio de una p agina web mientras el navegador continua cargando el resto de ella, entonces tiene una nocion de lo que los hilos pueden lograr. Cuando la preocupacion es la velocidad, se pueden escribir extensiones a Python en un lenguaje compilado como C o C++. Estas extensiones forman la base de muchos modulos en la biblioteca de Python. El mecanismo para enlazar funciones y datos es algo complejo. La herramienta SWIG (Simplied Wrapper and Interface Generator) simplica mucho estas tareas.
D.2 Libros generales de ciencias de la computacion recomendados Aqu hay algunos libros que contienen m as material sobre el lenguaje Python:
265
Core Python Programming, de Wesley Chun, es un gran libro de 750 p aginas, aproximadamente. La primera parte cubre las caracter sticas b asicas. La segunda introduce adecuadamente muchos topicos m as avanzados, incluyendo muchos de los que mencionamos anteriormente. Python Essential Reference, de David M. Beazley, es un pequeno libro, pero contiene mucha informacion sobre el lenguaje y la biblioteca est andar. Tambi en provee un excelente ndice. Python Pocket Reference, de Mark Lutz, realmente cabe en su bolsillo. Aunque no es tan comprensivo como Python Essential Reference; es una buena referencia para las funciones y modulos m as usados. Mark Lutz tambi en es el autor de Programming Python, uno de los primeros (y m as grandes) libros sobre Python que no est a dirigido a novatos. Su libro posterior Learning Python es m as pequeno y accesible. Python Programming on Win32, de Mark Hammond y Andy Robinson, se debe tener si pretende construir aplicaciones para el sistema operativo Windows. Entre otras cosas cubre la integracion entre Python y COM, construye una pequena aplicacion con wxPython, e incluso realiza guiones que agregan funcionalidad a aplicaciones como Word y Excel.
266
Lecturas adicionales recomendadas Programming Pearls, de Jon Bentley, es un libro cl asico. Comprende varios casos de estudio que aparecieron originalmente en la columna del autor en las Communications of the ACM. Los estudios examinan los compromisos que hay que tomar cuando se programa y por qu e tan a menudo es una mala idea apresurarse con la primera idea que se tiene para desarrollar un programa. Este libro es uno poco m as viejo que los otros (1986), as que los ejemplos est an escritos en lenguajes m as viejos. Hay muchos problemas para resolver, algunos traen pistas y otros su solucion. Este libro fue muy popular, incluso hay un segundo volumen. The New Turing Omnibus, de A.K Dewdney, hace una amable introduccion a 66 topicos en ciencias de la computacion, que van desde la computacion paralela hasta los virus de computador, desde escanograf as hasta algoritmos gen eticos. Todos son cortos e interesantes. Un libro anterior de Dewdney The Armchair Universe es una coleccion de art culos de su columna Computer Recreations en la revista Scientic American (Investigaci on y Ciencia), estos libros son una rica fuente de ideas para emprender proyectos. Turtles, Termites and Trafc Jams, de Mitchel Resnick, trata sobre el poder de la descentralizacion y como el comportamiento complejo puede emerger de la simple actividad coordinada de una multitud de agentes. Introduce el lenguaje StarLogo que permite escribir programas multiagentes. Los programas examinados demuestran el comportamiento complejo agregado, que a menudo es contraintuitivo. Muchos de estos programas fueron escritos por estudiantes de colegio y universidad. Programas similares pueden escribirse en Python usando hilos y gr acos simples. G odel, Escher y Bach, de Douglas Hofstadter. Simplemente, si usted ha encontrado magia en la recursion, tambi en la encontrar a en e ste best seller. Uno de los temas que trata Hofstadter es el de los ciclos extranos, en los que los patrones evolucionan y ascienden hasta que se encuentran a s mismos otra vez. La tesis de Hofstadter es que esos ciclos extranos son una parte esencial de muestra patrones como e lo que separa lo animado de lo inanimado. El stos en la musica de Bach, los cuadros de Escher y el teorema de incompletitud de Godel.
Ap endice E
Preamble
The purpose of this License is to make a manual, textbook, or other written document free in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modications made by others. This License is a kind of copyleft, which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software. We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.
268
E.1.
This License applies to any manual or other work that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. The Document, below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as you. A Modied Version of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modications and/or translated into another language. A Secondary Section is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Documents overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (For example, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical, or political position regarding them. The Invariant Sections are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. The Cover Texts are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Transparent copy of the Document means a machine-readable copy, represented in a format whose specication is available to the general public, whose contents can be viewed and edited directly and straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent le format whose markup has been designed to thwart or discourage subsequent modication by readers is not Transparent. A copy that is not Transparent is called Opaque. Examples of suitable formats for Transparent copies include plain ASCII without A markup, Texinfo input format, L TEX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML designed for human modication. Opaque formats include PostScript, PDF, proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machinegenerated HTML produced by some word processors for output purposes only. The Title Page means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such,
269
Title Page means the text near the most prominent appearance of the works title, preceding the beginning of the body of the text.
E.2.
Verbatim Copying
You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in Section 3. You may also lend copies, under the same conditions stated above, and you may publicly display copies.
E.3.
Copying in Quantity
If you publish printed copies of the Document numbering more than 100, and the Documents license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects. If the required texts for either cover are too voluminous to t legibly, you should put the rst ones listed (as many as t reasonably) on the actual cover, and continue the rest onto adjacent pages. If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a publicly accessible computernetwork location containing a complete Transparent copy of the Document, free of added material, which the general network-using public has access to download anonymously at no charge using public-standard network protocols. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you
270
distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public. It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.
E.4.
Modications
You may copy and distribute a Modied Version of the Document under the conditions of Sections 2 and 3 above, provided that you release the Modied Version under precisely this License, with the Modied Version lling the role of the Document, thus licensing distribution and modication of the Modied Version to whoever possesses a copy of it. In addition, you must do these things in the Modied Version: Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modications in the Modied Version, together with at least ve of the principal authors of the Document (all of its principal authors, if it has less than ve). State on the Title page the name of the publisher of the Modied Version, as the publisher. Preserve all the copyright notices of the Document. Add an appropriate copyright notice for your modications adjacent to the other copyright notices. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modied Version under the terms of this License, in the form shown in the Addendum below. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Documents license notice. Include an unaltered copy of this License.
E.4 Modications
271
Preserve the section entitled History, and its title, and add to it an item stating at least the title, year, new authors, and publisher of the Modied Version as given on the Title Page. If there is no section entitled History in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modied Version as stated in the previous sentence. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the History section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission. In any section entitled Acknowledgements or Dedications, preserve the sections title, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles. Delete any section entitled Endorsements. Such a section may not be included in the Modied Version. Do not retitle any existing section as Endorsements or to conict in title with any Invariant Section. If the Modied Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modied Versions license notice. These titles must be distinct from any other section titles. You may add a section entitled Endorsements, provided it contains nothing but endorsements of your Modied Version by various partiesfor example, statements of peer review or that the text has been approved by an organization as the authoritative denition of a standard. You may add a passage of up to ve words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modied Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you
272
may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one. The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modied Version.
E.5.
Combining Documents
You may combine the Document with other documents released under this License, under the terms dened in Section 4 above for modied versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodied, and list them all as Invariant Sections of your combined work in its license notice. The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work. In the combination, you must combine any sections entitled History in the various original documents, forming one section entitled History; likewise combine any sections entitled Acknowledgements, and any sections entitled Dedications. You must delete all sections entitled Endorsements.
E.6.
Collections of Documents
You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects. You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.
273
E.7.
A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, does not as a whole count as a Modied Version of the Document, provided no compilation copyright is claimed for the compilation. Such a compilation is called an aggregate, and this License does not apply to the other self-contained works thus compiled with the Document, on account of their being thus compiled, if they are not themselves derivative works of the Document. If the Cover Text requirement of Section 3 is applicable to these copies of the Document, then if the Document is less than one quarter of the entire aggregate, the Documents Cover Texts may be placed on covers that surround only the Document within the aggregate. Otherwise they must appear on covers around the whole aggregate.
E.8.
Translation
Translation is considered a kind of modication, so you may distribute translations of the Document under the terms of Section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License provided that you also include the original English version of this License. In case of a disagreement between the translation and the original English version of this License, the original English version will prevail.
E.9.
Termination
You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense, or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
274
spirit to the present version, but may differ in detail to address new problems or concerns. See http:///www.gnu.org/copyleft/. Each version of the License is given a distinguishing version number. If the Document species that a particular numbered version of this License .or any later version.applies to it, you have the option of following the terms and conditions either of that specied version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation.
Ap endice F
Pre ambulo
El proposito de esta licencia es permitir que un manual, libro de texto, u otro documento escrito sea libre en el sentido de libertad: asegurar a todo el mundo la libertad efectiva de copiarlo y redistribuirlo, con o sin modicaciones, de manera
276
comercial o no. En segundo t ermino, esta licencia proporciona al autor y al editor[2] una manera de obtener reconocimiento por su trabajo, sin que se le considere responsable de las modicaciones realizadas por otros. Esta licencia es de tipo copyleft, lo que signica que los trabajos derivados del documento deben a su vez ser libres en el mismo sentido. Complementa la Licencia Publica General de GNU, que es una licencia tipo copyleft disenada para el software libre. Hemos disenado esta licencia para usarla en manuales de software libre, ya que el software libre necesita documentacion libre: un programa libre debe venir con manuales que ofrezcan la mismas libertades que el software. Pero esta licencia no se limita a manuales de software; puede usarse para cualquier texto, sin tener en cuenta su tem atica o si se publica como libro impreso o no. Recomendamos esta licencia principalmente para trabajos cuyo n sea instructivo o de referencia.
F.1.
Aplicabilidad y deniciones
Esta licencia se aplica a cualquier manual u otro trabajo, en cualquier soporte, que contenga una nota del propietario de los derechos de autor que indique que puede ser distribuido bajo los t erminos de esta licencia. Tal nota garantiza en cualquier lugar del mundo, sin pago de derechos y sin l mite de tiempo, el uso de dicho trabajo segun las condiciones aqu estipuladas. En adelante la palabra Documento se referir a a cualquiera de dichos manuales o trabajos. Cualquier persona es un licenciatario y ser a referido como Usted. Usted acepta la licencia si copia. modica o distribuye el trabajo de cualquier modo que requiera permiso segun la ley de propiedad intelectual. Una Version Modicada del Documento signica cualquier trabajo que contenga el Documento o una porcion del mismo, ya sea una copia literal o con modicaciones y/o traducciones a otro idioma. Una Seccion Secundaria es un ap endice con t tulo o una seccion preliminar del Documento que trata exclusivamente de la relacion entre los autores o editores y el tema general del Documento (o temas relacionados) pero que no contiene nada que entre directamente en dicho tema general (por ejemplo, si el Documento es en parte un texto de matem aticas, una Seccion Secundaria puede no explicar nada de matem aticas). La relacion puede ser una conexion historica con el tema o temas relacionados, o una opinion legal, comercial, losoca, e tica o pol tica acerca de ellos. Las Secciones Invariantes son ciertas Secciones Secundarias cuyos t tulos son designados como Secciones Invariantes en la nota que indica que el documento es liberado bajo esta Licencia. Si una seccion no entra en la denicion de Secundaria, no puede designarse como Invariante. El documento puede no tener Secciones Invariantes. Si el Documento no identica las Secciones Invariantes, es que no las
277
tiene. Los Textos de Cubierta son ciertos pasajes cortos de texto que se listan como Textos de Cubierta Delantera o Textos de Cubierta Trasera en la nota que indica que el documento es liberado bajo esta Licencia. Un Texto de Cubierta Delantera puede tener como mucho 5 palabras, y uno de Cubierta Trasera puede tener hasta 25 palabras. Una copia Transparente del Documento, signica una copia para lectura en m aquina, representada en un formato cuya especicacion est a disponible al publico en general, apto para que los contenidos puedan ser vistos y editados directamente con editores de texto gen ericos o (para im agenes compuestas por puntos) con programas gen ericos de manipulacion de im agenes o (para dibujos) con algun editor de dibujos ampliamente disponible, y que sea adecuado como entrada para formateadores de texto o para su traduccion autom atica a formatos adecuados para formateadores de texto. Una copia hecha en un formato denido como Transparente, pero cuyo marcaje o ausencia de e l haya sido disenado para impedir o dicultar modicaciones posteriores por parte de los lectores no es Transparente. Un formato de imagen no es Transparente si se usa para una cantidad de texto sustancial. Una copia que no es Transparente se denomina Opaca. Como ejemplos de formatos adecuados para copias Transparentes est an ASCII puro sin marcaje, formato de entrada de Texinfo, formato de entrada de LaTeX, SGML o XML usando una DTD disponible publicamente, y HTML, PostScript o PDF simples, que sigan los est andares y disenados para que los modiquen personas. Ejemplos de formatos de imagen transparentes son PNG, XCF y JPG. Los formatos Opacos incluyen formatos propietarios que pueden ser le dos y editados unicamente en procesadores de palabras propietarios, SGML o XML para los cu ales las DTD y/o herramientas de procesamiento no est en ampliamente disponibles, y HTML, PostScript o PDF generados por algunos procesadores de palabras solo como salida. La Portada signica, en un libro impreso, la p agina de t tulo, m as las p aginas siguientes que sean necesarias para mantener legiblemente el material que esta Licencia requiere en la portada. Para trabajos en formatos que no tienen p agina de portada como tal, Portada signica el texto cercano a la aparicion m as prominente del t tulo del trabajo, precediendo el comienzo del cuerpo del texto. Una seccion Titulada XYZ signica una parte del Documento cuyo t tulo es precisamente XYZ o contiene XYZ entre par entesis, a continuacion de texto que traduce XYZ a otro idioma (aqu XYZ se reere a nombres de seccion espec cos mencionados m as abajo, como Agradecimientos, Dedicatorias , Aprobaciones o Historia. Conservar el T tulo de tal seccion cuando se modica el Documento signica que permanece una seccion Titulada XYZ segun esta denicion . El Documento puede incluir Limitaciones de Garant a cercanas a la nota donde se declara que al Documento se le aplica esta Licencia. Se considera que estas Limitaciones de Garant a est an incluidas, por referencia, en la Licencia, pero solo en
278
cuanto a limitaciones de garant a: cualquier otra implicacion que estas Limitaciones de Garant a puedan tener es nula y no tiene efecto en el signicado de esta Licencia.
F.2.
Copia literal
Usted puede copiar y distribuir el Documento en cualquier soporte, sea en forma comercial o no, siempre y cuando esta Licencia, las notas de copyright y la nota que indica que esta Licencia se aplica al Documento se reproduzcan en todas las copias y que usted no anada ninguna otra condicion a las expuestas en esta Licencia. Usted no puede usar medidas t ecnicas para obstruir o controlar la lectura o copia posterior de las copias que usted haga o distribuya. Sin embargo, usted puede aceptar compensacion a cambio de las copias. Si distribuye un numero sucientemente grande de copias tambi en deber a seguir las condiciones de la seccion 3. Usted tambi en puede prestar copias, bajo las mismas condiciones establecidas anteriormente, y puede exhibir copias publicamente.
F.3.
Copiado en cantidad
Si publica copias impresas del Documento (o copias en soportes que tengan normalmente cubiertas impresas) que sobrepasen las 100, y la nota de licencia del Documento exige Textos de Cubierta, debe incluir las copias con cubiertas que lleven en forma clara y legible todos esos Textos de Cubierta: Textos de Cubierta Delantera en la cubierta delantera y Textos de Cubierta Trasera en la cubierta trasera. Ambas cubiertas deben identicarlo a Usted clara y legiblemente como editor de tales copias. La cubierta debe mostrar el t tulo completo con todas las palabras igualmente prominentes y visibles. Adem as puede anadir otro material en las cubiertas. Las copias con cambios limitados a las cubiertas, siempre que conserven el t tulo del Documento y satisfagan estas condiciones, pueden considerarse como copias literales. Si los textos requeridos para la cubierta son muy voluminosos para que ajusten legiblemente, debe colocar los primeros (tantos como sea razonable colocar) en la verdadera cubierta y situar el resto en p aginas adyacentes. Si Usted publica o distribuye copias Opacas del Documento cuya cantidad exceda las 100, debe incluir una copia Transparente, que pueda ser le da por una m aquina, con cada copia Opaca, o bien mostrar, en cada copia Opaca, una direccion de red donde cualquier usuario de la misma tenga acceso por medio de protocolos publicos y estandarizados a una copia Transparente del Documento completa, sin material adicional. Si usted hace uso de la ultima opcion, deber a tomar las medidas
F.4 Modicaciones
279
necesarias, cuando comience la distribucion de las copias Opacas en cantidad, para asegurar que esta copia Transparente permanecer a accesible en el sitio establecido por lo menos un ano despu es de la ultima vez que distribuya una copia Opaca de esa edicion al publico (directamente o a trav es de sus agentes o distribuidores). Se solicita, aunque no es requisito, que se ponga en contacto con los autores del Documento antes de redistribuir gran numero de copias, para darles la oportunidad de que le proporcionen una version actualizada del Documento.
F.4.
Modicaciones
Puede copiar y distribuir una Version Modicada del Documento bajo las condiciones de las secciones 2 y 3 anteriores, siempre que usted libere la Version Modicada bajo esta misma Licencia, con la Version Modicada haciendo el rol del Documento, por lo tanto dando licencia de distribucion y modicacion de la Version Modicada a quienquiera posea una copia de la misma. Adem as, debe hacer lo siguiente en la Version Modicada: Usar en la Portada (y en las cubiertas, si hay alguna) un t tulo distinto al del Documento y de sus versiones anteriores (que deber an, si hay alguna, estar listadas en la seccion de Historia del Documento). Puede usar el mismo t tulo de versiones anteriores al original siempre y cuando quien las publico originalmente otorgue permiso. Listar en la Portada, como autores, una o m as personas o entidades responsables de la autor a de las modicaciones de la Version Modicada, junto con por lo menos cinco de los autores principales del Documento (todos sus autores principales, si hay menos de cinco), a menos que le eximan de tal requisito. Mostrar en la Portada como editor el nombre del editor de la Version Modicada. Conservar todas las notas de copyright del Documento. Anadir una nota de copyright apropiada a sus modicaciones, adyacente a las otras notas de copyright. Incluir, inmediatamente despu es de las notas de copyright, una nota de licencia dando el permiso para usar la Version Modicada bajo los t erminos de esta Licencia, como se muestra en la Adenda al nal de este documento. Conservar en esa nota de licencia el listado completo de las Secciones Invariantes y de los Textos de Cubierta que sean requeridos en la nota de Licencia del Documento original.
280
Licencia de documentacion libre de GNU Incluir una copia sin modicacion de esta Licencia. Conservar la seccion Titulada Historia, conservar su T tulo y anadirle un elemento que declare al menos el t tulo, el ano, los nuevos autores y el editor de la Version Modicada, tal como guran en la Portada. Si no hay una seccion Titulada Historia en el Documento, crear una estableciendo el t tulo, el ano, los autores y el editor del Documento, tal como guran en su Portada, anadiendo adem as un elemento describiendo la Version Modicada, como se establecio en la oracion anterior. Conservar la direccion en red, si la hay, dada en el Documento para el acceso publico a una copia Transparente del mismo, as como las otras direcciones de red dadas en el Documento para versiones anteriores en las que estuviese basado. Pueden ubicarse en la seccion Historia. Se puede omitir la ubicacion en red de un trabajo que haya sido publicado por lo menos cuatro anos antes que el Documento mismo, o si el editor original de dicha version da permiso. En cualquier seccion Titulada Agradecimientos o Dedicatorias, Conservar el T tulo de la seccion y conservar en ella toda la sustancia y el tono de los agradecimientos y/o dedicatorias incluidas por cada contribuyente. Conservar todas las Secciones Invariantes del Documento, sin alterar su texto ni sus t tulos. Numeros de seccion o el equivalente no son considerados parte de los t tulos de la seccion. Borrar cualquier seccion titulada Aprobaciones. Tales secciones no pueden estar incluidas en las Versiones Modicadas. No cambiar el t tulo de ninguna seccion existente a Aprobaciones ni a uno que entre en conicto con el de alguna Seccion Invariante. Conservar todas las Limitaciones de Garant a.
Si la Version Modicada incluye secciones o ap endices nuevos que caliquen como Secciones Secundarias y contienen material no copiado del Documento, puede opcionalmente designar algunas o todas esas secciones como invariantes. Para hacerlo, anada sus t tulos a la lista de Secciones Invariantes en la nota de licencia de la Version Modicada. Tales t tulos deben ser distintos de cualquier otro t tulo de seccion. Puede anadir una seccion titulada Aprobaciones, siempre que contenga unicamen te aprobaciones de su Version Modicada por otras fuentes por ejemplo, observaciones de peritos o que el texto ha sido aprobado por una organizacion como la denicion ocial de un est andar. Puede anadir un pasaje de hasta cinco palabras como Texto de Cubierta Delantera y un pasaje de hasta 25 palabras como Texto de Cubierta Trasera en la Version
281
Modicada. Una entidad solo puede anadir (o hacer que se anada) un pasaje al Texto de Cubierta Delantera y uno al de Cubierta Trasera. Si el Documento ya incluye un textos de cubiertas anadidos previamente por usted o por la misma entidad que usted representa, usted no puede anadir otro; pero puede reemplazar el anterior, con permiso expl cito del editor que agrego el texto anterior. Con esta Licencia ni los autores ni los editores del Documento dan permiso para usar sus nombres para publicidad ni para asegurar o implicar aprobacion de cualquier Version Modicada.
F.5.
Combinacion de documentos
Usted puede combinar el Documento con otros documentos liberados bajo esta Licencia, bajo los t erminos denidos en la seccion 4 anterior para versiones modicadas, siempre que incluya en la combinacion todas las Secciones Invariantes de todos los documentos originales, sin modicar, listadas todas como Secciones Invariantes del trabajo combinado en su nota de licencia. As mismo debe incluir la Limitacion de Garant a. El trabajo combinado necesita contener solamente una copia de esta Licencia, y puede reemplazar varias Secciones Invariantes id enticas por una sola copia. Si hay varias Secciones Invariantes con el mismo nombre pero con contenidos diferentes, haga el t tulo de cada una de estas secciones unico anadi endole al nal del mismo, entre par entesis, el nombre del autor o editor original de esa seccion, si es conocido, o si no, un numero unico. Haga el mismo ajuste a los t tulos de seccion en la lista de Secciones Invariantes de la nota de licencia del trabajo combinado. En la combinacion, debe combinar cualquier seccion Titulada Historia de los documentos originales, formando una seccion Titulada Historia; de la misma forma combine cualquier seccion Titulada Agradecimientos, y cualquier seccion Titulada Dedicatorias. Debe borrar todas las secciones tituladas Aprobaciones.
F.6.
Colecciones de documentos
Puede hacer una coleccion que conste del Documento y de otros documentos liberados bajo esta Licencia, y reemplazar las copias individuales de esta Licencia en todos los documentos por una sola copia que est e incluida en la coleccion, siempre que siga las reglas de esta Licencia para cada copia literal de cada uno de los documentos en cualquiera de los dem as aspectos. Puede extraer un solo documento de una de tales colecciones y distribuirlo individualmente bajo esta Licencia, siempre que inserte una copia de esta Licencia en el documento extra do, y siga esta Licencia en todos los dem as aspectos relativos a la copia literal de dicho documento.
282
F.7.
Una recopilacion que conste del Documento o sus derivados y de otros documentos o trabajos separados e independientes, en cualquier soporte de almacenamiento o distribucion, se denomina un agregado si el copyright resultante de la compilacion no se usa para limitar los derechos de los usuarios de la misma m as all a de lo que los de los trabajos individuales permiten. Cuando el Documento se incluye en un agregado, esta Licencia no se aplica a otros trabajos del agregado que no sean en s mismos derivados del Documento. Si el requisito de la seccion 3 sobre el Texto de Cubierta es aplicable a estas copias del Documento y el Documento es menor que la mitad del agregado entero, los Textos de Cubierta del Documento pueden colocarse en cubiertas que enmarquen solamente el Documento dentro del agregado, o el equivalente electronico de las cubiertas si el documento est a en forma electronica. En caso contrario deben aparecer en cubiertas impresas enmarcando todo el agregado.
F.8.
Traduccion
La Traduccion es considerada como un tipo de modicacion, por lo que usted puede distribuir traducciones del Documento bajo los t erminos de la seccion 4. El reemplazo de las Secciones Invariantes con traducciones requiere permiso especial de los duenos de derecho de autor, pero usted puede anadir traducciones de algunas o todas las Secciones Invariantes a las versiones originales de las mismas. Puede incluir una traduccion de esta Licencia, de todas las notas de licencia del documento, as como de las Limitaciones de Garant a, siempre que incluya tambi en la version en Ingl es de esta Licencia y las versiones originales de las notas de licencia y Limitaciones de Garant a. En caso de desacuerdo entre la traduccion y la version original en Ingl es de esta Licencia, la nota de licencia o la limitacion de garant a, la version original en Ingl es prevalecer a. Si una seccion del Documento est a Titulada Agradecimientos, Dedicatorias o Historia el requisito (seccion 4) de Conservar su T tulo (Seccion 1) requerir a, t picamente, cambiar su t tulo.
F.9.
Terminacion
Usted no puede copiar, modicar, sublicenciar o distribuir el Documento salvo por lo permitido expresamente por esta Licencia. Cualquier otro intento de copia, modicacion, sublicenciamiento o distribucion del Documento es nulo, y dar a por terminados autom aticamente sus derechos bajo esa Licencia. Sin embargo, los terceros que hayan recibido copias, o derechos, de usted bajo esta Licencia no ver an terminadas sus licencias, siempre que permanezcan en total conformidad con ella.
283
F.10.
De vez en cuando la Free Software Foundation puede publicar versiones nuevas y revisadas de la Licencia de Documentacion Libre GNU. Tales versiones nuevas ser an similares en esp ritu a la presente version, pero pueden diferir en detalles para solucionar nuevos problemas o intereses. Vea http://www.gnu.org/copyleft/. Cada version de la Licencia tiene un numero de version que la distingue. Si el Documento especica que se aplica una version numerada en particular de esta licencia o cualquier version posterior, usted tiene la opcion de seguir los t erminos y condiciones de la version especicada o cualquiera posterior que haya sido publicada (no como borrador) por la Free Software Foundation. Si el Documento no especica un numero de version de esta Licencia, puede escoger cualquier version que haya sido publicada (no como borrador) por la Free Software Foundation.
F.11.
Para usar esta licencia en un documento que usted haya escrito, incluya una copia de la Licencia en el documento y ponga el siguiente copyright y nota de licencia justo despu es de la p agina de t tulo: SU NOMBRE. Se concede permiso para copiar, disCopyright (c) ANO tribuir y/o modicar este documento bajo los t erminos de la Licencia de Documentacion Libre de GNU, Version 1.2 o cualquier otra version posterior publicada por la Free Software Foundation; sin Secciones Invariantes ni Textos de Cubierta Delantera ni Textos de Cubierta Trasera. Una copia de la licencia est a incluida en la seccion titulada GNU Free Documentation License. Si tiene Secciones Invariantes, Textos de Cubierta Delantera y Textos de Cubierta Trasera, reemplace la frase sin ... Trasera por esto: siendo las Secciones Invariantes LISTE SUS T ITULOS, siendo los Textos de Cubierta Delantera LISTAR, y siendo sus Textos de Cubierta Trasera LISTAR. Si tiene Secciones Invariantes sin Textos de Cubierta o cualquier otra combinacion de los tres, mezcle ambas alternativas para adaptarse a la situacion. Si su documento contiene ejemplos de codigo de programa no triviales, recomendamos liberar estos ejemplos en paralelo bajo la licencia de software libre que usted elija, como la Licencia Publica General de GNU (GNU General Public License), para permitir su uso en software libre.
284
Notas [1] Esta es la traduccion del Copyright de la Licencia, no es el Copyright de esta traduccion no autorizada. [2] La licencia original dice publisher, que es, estrictamente, quien publica, diferente de editor, que es m as bien quien prepara un texto para publicar. En castellano editor se usa para ambas cosas. [3] En sentido estricto esta licencia parece exigir que los t tulos sean exactamente Acknowledgements, Dedications, Endorsements e History, en ingl es.
286 car acter subrayado, 23 carga, 189, 215 Carta, 167 case base, 53 caso base, 55 chequeo de errores, 67 chequeo de tipos, 67 ciclo, 71, 80 anidado, 171 ciclo for, 82 condicion, 234 cuerpo, 71, 80 innito, 71, 234 recorrido, 82 while, 70 ciclo for, 82, 95 ciclo innito, 71, 80, 233, 234 ciclos en listas, 193 clase, 137, 145 Carta, 167 golsta, 212 JuegoSolterona, 183 ListaEnlazada, 196 madre, 178 ManoSolterona, 181 Nodo, 189 padre, 180 Pila, 200 Punto, 161 clase abstracta, 214 clase hija, 177 clase madre, 177, 178, 187 clase Nodo, 189 clase padre, 180 clase Punto, 161 clasicacion de car acteres, 88 clasicacion de car acteres, 88 clave, 115, 123 cliente, 199, 204 clonado, 118
Indice anal tico clonando, 99 clonar, 103 codicar, 168, 175 coercion, 43 tipo, 32, 122 coercion de tipos, 32 Cola, 214 implementacion con lista, 207 implementacion enlazada, 208 implementacion mejorada, 209 cola, 207 cola de prioridad, 207, 214 TAD, 211 Cola enlazada, 208, 214 Cola mejorada, 209 Cola TAD, 207 coleccion, 191, 200 columna, 102 coma otante, 137 comentario, 29, 30 comparable, 170 comparacion de cadenas, 84 de fracciones, 245 comparacion de cadenas, 84 compilador, 231 compilar, 12, 19 composicion, 28, 30, 34, 61, 167, 171 compresion, 122 concatenacion, 27, 30, 83, 85 de listas, 95 condicion, 55, 71, 234 condicional encadenados, 48 condicional encadenados, 48 constructor, 137, 145, 168 contador, 86, 89 conteo, 109 conversion, 32 tipo, 32 copia profunda, 145 copia supercial, 145
Indice anal tico copiado, 118, 143 correspondencia, 168 correspondencia de patrones, 113 cuerpo, 47, 55 ciclo, 71 cursor, 80 dato, 197 decrementar, 89 denicion circular, 63 funcion, 35 recursiva, 223 denicion circular, 63 denicion de funcion, 43 denicion recursiva, 223 del, 97 delimitador, 103, 131, 202, 204 denominador, 241 depuracion, 19, 231 depuracion (debugging), 14 desarrollo incremental, 59, 153 planeado, 153 desarrollo con prototipos, 151 desarrollo de programas encapsulamiento, 75 generalizacion, 75 desarrollo incremental, 68, 153 desarrollo incremental , 59 desarrollo incremental de programas, 232 desarrollo planeado, 153 desbordamiento, 121 desempeno, 209, 214 detencion, 233 determin stic, 113 diagrama de estados, 22, 30 diagrama de pila, 40, 43, 52 diccionario, 102, 115, 123, 130, 236 m etodos, 117 operaciones, 116 diccionarios, 115 m etodos, 117 operaciones sobre, 116 directorio, 131, 134 diseno orientado a objetos, 177 division entera, 32 division entera, 26, 30, 32 documentar, 197 Doyle, Arthur Conan, 15
287
ejecucion ujo de, 235 ejecucion condicional , 47 elemento, 91, 103 eliminando cartas, 174 en orden, 219, 228 encapsulamiento, 75, 142, 199, 204 encapsular, 80 encriptar, 168 encurtido, 131, 134 enlace, 197 EnOrden, 218 enteros largos, 121 enteros largos, 121 error de tiempo de ejecucion, 53 en tiempo de compilacion, 231 en tiempo de ejecucion, 15 sem antica, 231 sem antico, 237 sintaxis, 14, 231 tiempo de ejecucion, 231 error (bug), 14 error de tiempo de ejecucion, 53, 82 error en tiempo de compilacion, 231 error en tiempo de ejecucion, 15, 19, 82, 85, 93, 106, 117, 119, 121, 126, 129, 231, 235 error sem antico, 15, 19, 107, 231 error sem antico , 237 error sint actico, 14, 19, 231
288 error(bug), 19 errores manejo de, 225 espacio en blanco, 89 espacios en blanco, 88 estilo de programacio funcional, 153 estilo de programacion funcional, 150 estructura anidada, 167 Estructura de datos gen erica, 200 estructura de datos gen erica, 201 recursiva, 189, 197 Estructura de datos gen erica, 200 estructura de datos gen erica, 201 estructura de datos recursiva, 189, 197 estructuras de datos recursivas, 216 estructuras de datos recursivas, 216 Euclides, 244 excepcion, 15, 19, 132, 134, 231, 235 expresion, 26, 30, 202 booleana, 45, 55 grande y peluda, 238 expresion Booleana, 45 expresion booleana, 55 expresion regular, 202 facilitador, 197 gura, 167 la, 102 oat, 21 Flujo de Ejecucion, 235 ujo de ejecucion, 37, 43 formal lenguaje, 16 forzado de tipo de datos, 122 frabjuoso, 63 fraccion, 241 fracciones comparacion de, 245 multiplicacion, 242
Indice anal tico suma, 243 funcion, 35, 43, 79, 147, 156 argumento, 38 booleana, 175 composicion, 34, 61 facilitadora, 195 factorial, 64 matem atica, 33 par ametro, 38 recursiva, 52 tupla como valor de retorno, 107 funcion booleana, 62, 175 funcion de Fibonacci, 120 funcion denicion, 35 funcion facilitadora, 195 funcion factorial, 64, 67 funcion gama, 67 funcion join, 102 funcion matem atica, 33 funcion pura, 148, 153 funcion split, 102 generalizacion, 75, 142, 152 generalizar, 80 golsta, 212 gr aco de llamadas, 120 guarda, 68 guion, 19 herencia, 177, 187 histograma, 112, 113, 122 Holmes, Sherlock, 15 identidad, 140 igualdad, 140 igualdad profunda, 140, 145 igualdad supercial, 140, 145 implementacion Cola, 207 imprimiendo manos de cartas, 180 imprimir objeto, 139
Indice anal tico objeto mazo, 171 objetos, 156 incrementar, 89 IndexError, 236 inja, 202, 204, 217 inmutable, 105 instancia, 139, 142, 145 objeto, 138, 156, 170 instancia de un objeto, 138 instanciacion, 138 instanciar, 145 instruccion, 13 int, 21 Intel, 72 intercambiar, 174 interfaz, 200, 214 interpretar, 12, 19 Invariante, 197 invariante, 197 Invariante de objetos, 197 invocar, 123 invocar m etodos, 117 irracional, 246 iteracion, 69, 70, 80 juego de animales, 225 juego de animales, 225 KeyError, 236 La funcion de Fibonacci, 66 lanzar excepcion, 134 lanzar una excepcion, 132 lenguaje, 139 alto nivel, 12 bajo nivel, 12 completo, 63 programacion, 11 lenguaje completo, 63 lenguaje de alto nivel, 12, 19 lenguaje de bajo nivel, 12, 19 lenguaje de programacion, 11
289 lenguaje de programacion orientado a objetos, 155, 166 lenguaje formal, 16, 19 lenguaje natural, 16, 19, 139 lenguaje seguro, 15 lexema, 202, 204, 220 lexicogr aco, 83 Linux, 16 lista, 91, 103, 189 anidada, 91, 101, 102, 118 bien formada, 197 ciclo, 193 ciclo for, 95 clonando, 99 como par ametro, 191 de objetos, 171 elemento, 92 enlazada, 189, 197 imprimir, 191 innita, 193 longitud, 93 modicando, 194 mutable, 96 pertenencia , 94 recorrido, 191 recorrido de una, 93 recorrido recursivo, 192 segmento, 96 lista anidada, 103, 118 lista enlazada, 189, 197 lista innita, 193 ListaEnlazada, 196 listas como par ametros, 100 imprimiendo al rev es, 192 listas anidadas, 101 Literalidad, 17 llamada funcion, 31 llamada a funcion, 31, 43 logaritmo, 72 longitud, 93
290 m aximo divisor comun, 244, 246 m etodo, 117, 123, 147, 156, 166 auxiliar, 195 de inicializacion, 160, 171 de lista, 171 de solucion de problemas, 10 facilitador, 195 invocacion, 117 lista, 123 m etodo append, 171 m etodo auxiliar, 195, 197 m etodo de inicializacion, 160, 166, 171 m etodo de lista, 123, 171 m etodo de solucion de problemas, 4, 10 m etodo facilitador, 195 m etodos sobre diccionarios, 117 modulo, 33, 43, 87 copy, 143 string, 89 modulo copy, 143 modulo string, 87, 89 manejar excepcion, 134 manejar una excepcion, 132 manejo de errores, 225 marco, 40, 43, 52 marco de funcion, 40, 43, 52 matriz, 102 dispersa, 118 mayusculas, 88 mazo, 171 McCloskey, Robert, 83 mensajes de error, 231 meter, 201 minusculas, 88 mismidad, 139 modelo mental, 237 modelo mental, 237 modicadora, 149, 153 modicando listas, 194 multiplicacion de fracciones, 242
Indice anal tico multiplicacion escalar, 162, 166 mutable, 85, 89, 105 lista, 96 numero aleatorio, 108 numero aleatorio, 108 NameError, 235 natural lenguaje, 16 negacion, 246 negacion unaria, 246 nivel, 215, 228 nodo, 189, 197, 215, 228 nodo de un a rbol, 215 nodo hermano, 228 nodo hijo, 215, 228 nodo hoja, 215, 228 nodo padre, 215, 228 nodo ra z, 215, 228 None, 58, 68 notacion de punto, 117 notacion punto, 33, 43, 157, 160 nueva l nea, 80 numerador, 241 objeto, 98, 103, 137, 145 lista de, 171 mutable, 142 objeto instancia, 156, 170 objeto mutable, 142 operacion sobre listas, 95 operacion sobre cadenas, 27 operacion sobre listas, 97 operaciones sobre listas, 95 operador, 26, 30 binario, 217, 228 condicional, 170 corchete, 81 de formato, 213 formato, 128, 134, 235
Indice anal tico in, 94, 174 logico, 45 logicos, 46 residuo, 45, 179 operador binario, 217, 228 operador condicional , 170 operador corchete, 81 operador de formato, 128, 134, 213, 235 operador in, 94, 174 operador logico, 45 operador matem atico, 242 operador residuo, 45, 55, 179 operador unario, 246 operadores sobrecarga, 162 sobrecarga de, 242 operadores logicos, 46 operadores sobrecarga de, 162 operando, 26, 30 orden, 170 orden de evaluacion, 238 orden de las operaciones, 26 orden parcial, 170 orden total, 170 palabra reservada, 23 palabra reservada, 23, 24, 30 par clave-valor, 115, 123 par ametro, 38, 43, 100, 139 lista, 100 patron, 86 patron computacional, 86 Pentium, 72 PEPS, 207, 214 Pila, 200 pila, 200 pista, 120, 123 plan de desarrollo, 80 Poes a, 17 pol tica de atencion, 214 pol tica para meter, 207
291 polimorca, 166 polimorsmo, 164 port atil, 12 portabilidad, 19 postja, 202, 204, 217 Postorden, 218 postorden, 219, 228 precedencia, 30, 238 precondicion, 193, 197 preja, 219, 228 Preorden, 218, 219 preorden, 228 print sentencia, 18, 19 prioridad, 212 problema, 10 solucion, 10 problema computacional, 7 producto, 223 producto punto, 162, 166 programa, 19 programacion orientada a objetos, 155, 177 programas desarrollo de, 80 prompt, 54, 55 Prosa, 17 proveedor, 199, 204 pseudoaleatorio, 113 pseudocodigo, 244 punto otante, 30 racional, 241 rama, 48, 55 ramicacion condicional, 47 random, 173 randrange, 173 recorrer, 217 recorrido, 86, 89, 95, 182, 191, 192, 212, 218 lista, 93 recorrido de una lista, 103
292 recorrido eureka, 86 recorridos, 82 rect angulo, 141 recuento, 122 recursion, 50, 52, 55, 63, 65, 217, 218 caso base, 53 innita, 53, 67 recursion Innita, 234 recursion innita, 53, 55, 67, 233 Redundancia, 17 referencia, 189 alias, 99 incrustada, 189, 197 referencia incrustada, 189, 197, 215 reglas de precedencia, 26, 30 repartiendo cartas, 179 repeticion de listas, 96 restriccion, 10 rol variable, 193 ruta, 131 sacar, 201 salto de fe, 65, 192 secuencia, 91, 103 secuencia de escape, 74, 80 segmento, 84, 89, 96 seguro lenguaje, 15 sem antica, 15, 19 sem antico error, 15 sentencia, 30 asignacion, 22, 69 bloque, 47 break, 127, 134 compuesta, 47 condicional, 55 continue, 128, 134 except, 132 pass, 47
Indice anal tico print, 236 return, 50, 238 try, 132 while, 70 sentencia break, 127, 134 sentencia compuesta, 47, 55 bloque de sentencias, 47 cabecera, 47 cuerpo, 47 sentencia condicional , 55 sentencia continue, 128, 134 sentencia except, 132, 134 sentencia pass, 47 sentencia print, 18, 19, 236 sentencia return, 50, 238 sentencia try, 132 sentencia while, 70 serie aritm etica, 73 serie geom etrica, 73 simplicar, 244, 246 singleton, 195, 197 sint actica, 14 sintaxis, 19 sobrecarga, 166, 242 operador, 212 sobrecarga de operadores, 166, 170, 212 sobrecargar, 170 sobreescribir, 166 solucion a un problema, 10 solucion de problemas, 1, 10, 19 subclase, 177, 180, 187 subexpresion, 224 suma, 223 de fracciones, 243 syntax, 232 tab, 80 tabla, 72 bidimensional, 74 TAD, 199, 204 Cola, 207 cola de prioridad, 207, 211
Indice anal tico Pila, 200 teorema fundamental de la ambiguedad, 193 teorema fundamental de la ambiguedad, 197 tiempo constante, 209, 214 tiempo lineal, 209, 214 tipo, 21, 30 cadena, 21 oat, 21 int, 21 tipo de dato compuesto, 81 denido por el usuario, 241 inmutable, 105 tupla, 105 tipo de dato compuesto, 81, 89 tipo de datos compuesto, 137 denido por el usuario, 137 diccionario, 115 tipo de datos compuestos, 137 tipo de datos denido por el usuario, 137 tipo funcion modicadora, 149 pura, 148 tipo inmutable, 113 tipo mutable, 113 tipos abstractos de datos, 199 tipos de datos enteros largos, 121 traza, 133 trazado inverso, 41, 43, 53, 235 try, 134 tupla, 105, 107, 113 Turing, Alan, 63 Turing, T esis de , 63 TypeError, 235 unidad, 19 uso de alias, 143 valor, 21, 30, 98, 167 tupla, 107 valor de retorno, 31, 43, 57, 68, 142 tupla, 107 variable, 22, 30 de ciclo, 179 local, 39, 76 roles, 193 temporal, 58, 68, 238 variable de ciclo, 80, 179, 191 variable local, 39, 43, 76 variable temporal, 58, 68, 238
293