Next Up Previous Hi Index

Chapter 7

Cadenas

7.1 Un tipo de datos compuesto

Hasta el momento hemos visto tres tipos: int, float, y string. Las cadenas son cuantitativamente diferentes de los otros dos porque están hechas de piezas menores: caracteres.

Los tipos que comprenden piezas menores se llaman tipos de datos compuestos. Dependiendo de qué estemos haciendo, podemos querer tratar un tipo compuesto como una única cosa o acceder a sus partes. Esta ambigüedad es útil.

El operador corchete selecciona un carácter suelto de una cadena.

>>> fruta = "banana"
>>> letra = fruta[1]
>>> print letra

La expresión fruta[1] selecciona el carácter número 1 de fruta. La variable letra apunta al resultado. Cuando mostramos letra, nos encontramos con una sorpresa:

a

La primera letra de "banana" no es a. A no ser que usted sea un programador. Por perversas razones, los científicos de la computación siempre empiezan a contar desde cero. La 0-sima letra ("cerósima") de "banana"es b. La 1-ésima ("unésima") es a, y la 2-ésima ("dosésima") letra es n.

Si quiera la cerósima letra de una cadena, simplemente pone 0, o cualquier expresión de valor 0, entre los corchetes:

>>> letra = fruta[0]
>>> print letra
b

A la expresión entre corchetes se le llama índice. Un índice identifica a un miembro de un conjunto ordenado, en este caso el conjunto de caracteres de la cadena. El índice indica cuál quiere usted, de ahí el nombre. Puede ser cualquier expresión entera.

7.2 Longitud

La función len devuelve el número de caracteres de una cadena:

>>> fruta = "banana"
>>> len(fruta)
6

Para obtener la última letra de una cadena puede sentirse tentado a probar algo como esto:

longitud = len(fruta)
ultima = fruta[longitud]       # ERROR!

Eso no funcionará. Provoca un error en tiempo de ejecución IndexError:
string index out of range
. La razón es que no hay una sexta letra en "banana". Como empezamos a contar por cero, las seis letras están numeradas del 0 al 5. Para obtener el último carácter tenemos que restar 1 de longitud:

longitud = len(fruta)
ultima = fruta[longitud-1]

De forma alternativa, podemos usar índices negativos, que cuentan hacia atrás desde el final de la cadena. La expresión fruta[-1] nos da la última letra. fruta[-2] nos da la penúltima, y así.

7.3 Recorrido y el bucle \tt{for

\label{for} \index{recorrido} \index{bucle!recorrido} \index{bucle for} \index{bucle!for} Muchos cálculos incluyen el proceso de una cadena carácter a carácter. A menudo empiezan por el principio, seleccionan cada carácter por turno, hacen algo con él y siguen hasta el final. Este patrón de proceso se llama {\bf recorrido}. Una forma de codificar un recorrido es una sentencia \verb|while|: \adjustpage{2} \beforeverb \begin{verbatim} indice = 0 while indice < len(fruta): letra = fruta[indice] print letra indice = indice + 1 \end{verbatim} \afterverb % Este bucle recorre la cadena y muestra cada letra en una línea distinta. La condición del bucle es \verb|indice < len(fruta)|, de modo que cuando \verb|indice| es igual a la longitud de la cadena, la condición es falsa y no se ejecuta el cuerpo del bucle. El último carácter al que se accede es el que tiene el índice \verb|len(fruta)-1|, que es el último carácter de la cadena. \begin{quote} {\em Como ejercicio, escriba una función que tome una cadena como argumento y entregue las letras en orden inverso, una por línea.} \end{quote} Es tan habitual usar un índice para recorrer un conjunto de valores que Python facilita una sintaxis alternativa más simple: el bucle \verb|for|: \beforeverb \begin{verbatim} for car in fruta: print car \end{verbatim} \afterverb % Cada vez que recorremos el bucle, se asigna a la variable \verb|car| el siguiente carácter de la cadena. El bucle continúa hasta que no quedan caracteres. \index{concatenación} \index{abeced'arico} \index{McCloskey, Robert} \index{{\em Make Way for Ducklings}} El ejemplo siguiente muestra cómo usar la concatenación junto a un bucle {\tt for} para generar una serie abecedárica. ``Abecedárica'' es la serie o lista en la que cada uno de los elementos aparece en orden alfabético. Por ejemplo, en el libro de Robert McCloskey {\em Make Way for Ducklings (Dejad paso a los patitos)}, los nombres de los patitos son Jack, Kack, Lack, Mack, Nack, Ouack, Pack, y Quack. Este bucle saca esos nombres en orden: \beforeverb \begin{verbatim} prefijos = "JKLMNOPQ" sufijo = "ack" for letra in prefijos: print letra + sufijo \end{verbatim} \afterverb % La salida del programa es: \beforeverb \begin{verbatim} Jack Kack Lack Mack Nack Oack Pack Qack \end{verbatim} \afterverb % Por supuesto, esto no es del todo correcto, porque ``Ouack'' y ``Quack'' no están correctamente escritos. \begin{quote} {\em Como ejercicio, modifique el programa para corregir este error.} \end{quote} \section{Porciones de cadenas} \label{slice} \index{porción} \index{cadena!porción} Llamamos {\bf porción} a un segmento de una cadena. La selección de una porción es similar a la selección de un carácter: \beforeverb \begin{verbatim} >>> s = "Pedro, Pablo, y María" >>> print s[0:5] Pedro >>> print s[7:12] Pablo >>> print s[15:20] María \end{verbatim} \afterverb % El operador \verb|[n:m]| devuelve la parte de la cadena desde el enésimo carácter hasta el ``emésimo'', incluyendo el primero pero excluyendo el último. Este comportamiento contradice a nuestra intuición; tiene más sentido si imagina los índices señalando {\em entre} los caracteres, como en el siguiente diagrama: \beforefig \centerline{\psfig{figure=../illustrations/banana.eps}} \afterfig Si omite el primer índice (antes de los dos puntos), la porción comienza al principio de la cadena. Si omite el segundo índice, la porción llega al final de la cadena. Así: \beforeverb \begin{verbatim} >>> fruta = "banana" >>> fruta[:3] 'ban' >>> fruta[3:] 'ana' \end{verbatim} \afterverb % ¿Qué cree usted que significa \verb|s[:]|? \section{Comparación de cadenas} \index{comparación de cadenas} \index{comparación!cadenas} Los operadores de comparación trabajan sobre cadenas. Para ver si dos cadenas son iguales: \beforeverb \begin{verbatim} if palabra == "banana": print "Sí, no tenemos bananas!" \end{verbatim} \afterverb % \adjustpage{-2} Otras operaciones de comparación son útiles para poner palabras en orden alfabético: \beforeverb \begin{verbatim} if palabra < "banana": print "Tu palabra," + palabra + ", va antes de banana." elif palabra > "banana": print "Tu palabra," + palabra + ", va después de banana." else: print "Sí, no tenemos bananas!" \end{verbatim} \afterverb % Sin embargo, debería usted ser consciente de que Python no maneja las mayúsculas y minúsculas como lo hace la gente. Todas las mayúsuculas van antes de la minúsculas. Como resultado de ello: \beforeverb \begin{verbatim} Tu palabra, Zapato, va antes de banana. \end{verbatim} \afterverb % Una forma común de abordar este problema es convertir las cadenas a un formato estándar, como pueden ser las minúsculas, antes de realizar la comparación. Un problema mayor es hacer que el programa se dé cuenta de que los zapatos no son frutas. \section{Las cadenas son inmutables} \index{mutable} \index{cadena inmutable} \index{cadena!inmutable} Es tentador usar el operador \verb|[]| en el lado izquierdo de una asignación, con la intención de cambiar un carácter en una cadena. Por ejemplo: \beforeverb \begin{verbatim} saludo = "Hola, mundo" saludo[0] = 'M' # ERROR! print saludo \end{verbatim} \afterverb % En lugar de presentar la salida \verb|Mola, mundo|, este código presenta el siguiente error en tiempo de ejecución {\tt TypeError: object doesn't support item assignment}. \index{error en tiempo de ejecución} Las cadenas son {\bf inmutables}, lo que significa que no puede cambiar una cadena existente. Lo más que puede hacer es crear una nueva cadena que sea una variación de la original: \beforeverb \begin{verbatim} saludo = "Hola, mundo" nuevoSaludo = 'M' + saludo[1:] print nuevoSaludo \end{verbatim} \afterverb % Aquí la solución es concatenar una nueva primera letra a una porción de \verb|saludo|. Esta operación no tiene efectos sobre la cadena original. \index{concatenación} \adjustpage{-2} \section{Una función ``encuentra''} \label{encuentra} \index{recorrido} \index{recorrido eureka} \index{patrón} \index{patrón computacional} ¿Qué hace la siguiente función? \beforeverb \begin{verbatim} def encuentra(cad, c): indice = 0 while indice < len(cad): if cad[indice] == c: return indice indice = indice + 1 return -1 \end{verbatim} \afterverb % En cierto sentido, \verb|encuentra| es lo contrario del operador \verb|[]|. En lugar de tomar un índice y extraer el carácter correspondiente, toma un carácter y encuentra el índice donde aparece el carácter. Si el carácter no se encuentra, la función devuelve {\tt -1}. Este es el primer ejemplo que hemos visto de una sentencia \verb|return| dentro de un bucle. Si \verb|cad[indice] == c|, la función vuelve inmediatamente, escapando del bucle prematuramente. Si el carácter no aparece en la cadena, el programa sale del bucle normalmente y devuelve \verb|-1|. Este patrón de computación se llama a veces un recorrido ``eureka'' porque en cuanto encontramos lo que buscamos, podemos gritar ``¡Eureka!'' y dejar de buscar. \begin{quote} {\em A modo de ejercicio, modifique la función \verb|encuentra| para que acepte un tercer parámetro, el índice de la cadena donde debería empezar a buscar.} \end{quote} \section{Bucles y conteo} \label{contador} \index{contador} \index{patrón} El programa que sigue cuenta el número de veces que la letra \verb|a| aparece en una cadena: \beforeverb \begin{verbatim} fruta = "banana" cuenta = 0 for car in fruta: if car == 'a': cuenta = cuenta + 1 print cuenta \end{verbatim} \afterverb % Este programa muestra otro patrón de computación llamado {\bf contador}. La variable \verb|cuenta| se incializa a 0 y luego se incrementa cada vez que se encuentra una \verb|a|. ({\bf Incrementar} es aumentar en uno; es lo contario de {\bf decrementar}, y sin relación alguna con ``excremento'', que es un nombre.) Al salir del bucle, \verb|cuenta| contiene el resultado -- el número total de \verb|a|es. \begin{quote} {\em Como ejercicio, encapsule este código en una función llamada {\tt cuentaLetras}, y generalícela de forma que acepte la cadena y la letra como parámetros.} \end{quote} \begin{quote} {\em Como un segundo ejercicio, reescriba esta función para que en lugar de recorrer la cadena, use la versión de tres parámetros de {\tt encuentra del anterior}.} \end{quote} \section{El módulo ``string''} \index{módulo} \index{módulo string} El módulo \verb|string| contiene funciones útiles para manipular cadenas. Como es habitual, tenemos que importar el módulo antes de poder usarlo: \beforeverb \begin{verbatim} >>> import string \end{verbatim} \afterverb % El módulo \verb|string| incluye una función llamada \verb|find| que hace lo mismo que la función \verb|encuentra| que escribimos. Para llamarla debemos especificar el nombre del módulo y el nombre de la función por medio de la notación de punto. \beforeverb \begin{verbatim} >>> fruta = "banana" >>> indice = string.find(fruta, "a") >>> print indice 1 \end{verbatim} \afterverb % Este ejemplo demuestra uno de los beneficios de los módulos: ayudan a evitar las colisiones entre los nombres de las funciones predefinidas y las definidas por el usuario. Al usar la notación de punto podríamos especificar qué versión de \verb|find| queremos en caso de haberle daddo un nombre en inglés a nuestra función. En realidad, \verb|string.find| es más general que nuestra versión. Para empezar, puede encontrar subcadenas, no sólo caracteres: \beforeverb \begin{verbatim} >>> string.find("banana", "na") 2 \end{verbatim} \afterverb % Además, acepta un argumento adicional que especifica el índice en el que debería comenzar: \beforeverb \begin{verbatim} >>> string.find("banana", "na", 3) 4 \end{verbatim} \afterverb % O puede tomar dos argumentos adicionales que especifican un intervalo de índices: \beforeverb \begin{verbatim} >>> string.find("sus", "s", 1, 2) -1 \end{verbatim} \afterverb % En este ejemplo, la búsqueda falla porque la letra {\em s} no aparece en el intervalo de índices desde \verb|1| hasta \verb|2| (sin incluir {\tt 2}). \section{Clasificación de caracteres} \label{in} \index{clasificación de caracteres} \index{clasificación!carácter} \index{uppercase} \index{lowercase} \index{whitespace} A menudo viene bien examinar un carácter y comprobar si es una letra mayúscula o minúscula, o si es un carácter o un dígito. El módulo \verb|string| proporciona varias constantes que son útiles para estos menesteres. La cadena \verb|string.lowercase| contiene todas las letras que el sistema considera como minúsculas. De forma similar, \verb|string.uppercase| contiene todas las mayúsculas. Pruebe lo que sigue y vea qué obtiene: \beforeverb \begin{verbatim} >>> print string.lowercase >>> print string.uppercase >>> print string.digits \end{verbatim} \afterverb % Podemos usar estas constantes y \verb|find| para clasificar caracteres. Por ejemplo, si \verb|find(lowercase, c)| devuelve un valor que no sea {\tt -1}, entonces \verb|c| es una minúscula: \beforeverb \begin{verbatim} def esMinuscula(c): return find(string.lowercase, c) != -1 \end{verbatim} \afterverb % Alternativamente, podemos aprovecharnos del operador \verb|in|, que determina si un carácter aparece en una cadena: \beforeverb \begin{verbatim} def esMinuscula(c): return c in string.lowercase \end{verbatim} \afterverb % Como una alternativa más, podemos usar el operador de comparación, aunque esta solución sólo sea práctica para el alfabeto inglés: \beforeverb \begin{verbatim} def esMinuscula(c): return 'a' <= c <= 'z' \end{verbatim} \afterverb % Si \verb|c| está entre {\em a} y {\em z}, tiene que ser una minúscula. \begin{quote} {\em Como ejercicio, explique qué versión de \verb|esMinuscula| cree que es más rápida. ¿Puede pensar en otras razones aparte de la velocidad para preferir una sobre la otra?} \end{quote} Otra constante definida en el módulo \verb|string| puede sorprenderle cuando la imprima: \beforeverb \begin{verbatim} >>> print string.whitespace \end{verbatim} \afterverb % Los caracteres de {\bf whitespace} mueven el cursor sin imprimir nada. Crean los espacios en blanco entre los caracteres visibles (al menos sobre papel blanco). La constante \verb|string.whitespace| contiene todos los caracteres de espacio en blanco, incluídos espacio, tabulador (\verb+\t+), y salto de línea (\verb+\n+). \index{módulo string} \index{módulo!string} Hay otras funciones útiles en el módulo \verb|string|, pero este libro no pretende ser un manual de referencia. Por otra parte, la {\em Referencia de la Biblioteca de Python} sí lo es. Junto con un montón más de documentación, está disponible en el sitio web de Python, {\tt www.python.org}. \index{{\em Referencia de la Biblioteca de Python}} \section{Glosario} \begin{description} \item[tipo de datos compuesto:] Un tipo de datos en el que los valores están hechos de componentes o elementos que son a su vez valores. \item[recorrer:] Realizar de forma iterativa una operación similar sobre cada uno de los elementos de un conjunto. \item[índice:] Una variable o valor usado para seleccionar un miembro de un conjunto ordenado, como puede ser un carácter de una cadena. \item[porción:] Una parte de una cadena especificada por un intervalo de índices. \item[mutable:] Un tipo de datos compuesto a cuyos elementos se les puede asignar nuevos valores. \item[contador:] Una variable usada para contar algo, normalmente inicializado a cero e incrementado posteriormente. \item[incrementar:] Aumentar el valor de una variable en una unidad. \item[decrementar:] Disminuir el valor de una variable en una unidad. \item[espacio en blanco:] Cualquiera de los caracteres que mueven el cursor sin imprimir caracteres visibles. La constante \verb|string.whitespace| contiene todos los caracterse de espacio en blanco. \index{tipo compuesto de datos} \index{recorrer} \index{índice} \index{porción} \index{mutable} \index{contador} \index{incrementar} \index{decrementar} \index{espacio en blanco} \end{description}


Next Up Previous Hi Index