Next Up Previous Hi Index

Chapter 4

Condicionales y recursividad

4.1 El operador módulo

El operador módulo funciona con enteros (y expresiones enteras), y devuelve el resto de dividir el primer operando entre el segundo. En Python, el operador de módulo es el signo de tanto por ciento (\texttt{%). La sintaxis es la misma de los otros operadores: \begin{verbatim} >>> cociente = 7 / 3 >>> print cociente 2 >>> resto = 7 % 3 >>> print resto 1 \end{verbatim} Así, 7 dividido entre 3 da 2 con 1 de resto. El operador de módulo resulta ser soprendentemente útil. Por ejemplo, puede comprobar si un número es divisible entre otro: si \texttt{x \% y} es cero, entonces \texttt{x} es divisible entre \texttt{y}. También puede usar el operador módulo para extraer el dígito más a la derecha de un número. Por ejemplo, \texttt{x \% 10} devuelve el dígito más a la derecha de \texttt{x} (en base 10). De forma similar, \texttt{x \% 100} devuelve los dos últimos dígitos. \section{Expresiones booleanas} \index{expresión booleana} \index{booleana!expresión} \index{operador lógico} \index{lógico!operador} Una {\bf expresión booleana} es una expresión que es cierta o falsa. En Python, una expresión que es cierta tiene el valor 1, y una expresión que es falsa tiene el valor 0. El operador {\tt ==} compara dos valores y entrega una expresión booleana: \beforeverb \begin{verbatim} >>> 5 == 5 1 >>> 5 == 6 0 \end{verbatim} \afterverb % En la primera sentencia, los dos operandos son iguales, así que la expresión se evalúa como 1 (verdadero); en la segunda sentencia, 5 no es igual a 6, así que obtenemos 0 (falso). El operador {\tt ==} es uno de los {\bf operadores de comparación}; los otros son: \beforeverb \begin{verbatim} x != y # x no es igual a y x > y # x es mayor que y x < y # x es menor que y x >= y # x es mayor o igual que y x <= y # x es menor o igual que y \end{verbatim} \afterverb % Aunque probablemente estas operaciones le resulten familiares, los símbolos en Python son diferentes de los matemáticos. Un error habitual es utilizar un signo igual sencillo ({\tt =}) en lugar del doble ({\tt ==}). Recuerde que {\tt =} es un operador de asignación y {\tt ==} es un operador de comparación. Además, no existen \verb|=<| ni \verb|=>|. \section {Operadores lógicos} \index{operador lógico} \index{lógico!operador} Hay tres {\bf operadores lógicos}: {\tt and}, {\tt or}, y {\tt not}. La semántica (significado) de estos operadores es similar a sus significados en inglés. Por ejemplo, {\tt x > 0 and x < 10} es verdadero sólo si {\tt x} es mayor que 0 {\em y} menor que 10. {\tt n\%2 == 0 or n\%3 == 0} es verdadero si {\em cualquiera} de las condiciones es verdadera, o sea, si el número es divisible por 2 {\em o} por 3. Finalmente, el operador {\tt not} niega una expresión booleana, de forma que {\tt not(x > y)} es cierto si {\tt (x > y)} es falso, o sea, si {\tt x} es menor o igual que {\tt y}. Hablando estrictamente, los operandos de los operadores lógicos deberían ser expresiones booleanas, pero Python no es muy estricto. Cualqueir número que no sea cero se interpreta como ``verdadero''. \beforeverb \begin{verbatim} >>> x = 5 >>> x and 1 1 >>> y = 0 >>> y and 1 0 \end{verbatim} \afterverb % En general, este tipo de cosas no se considera buen estilo. Si quiere comparar un valor con cero, debería hacerlo explícitamente. \section{Ejecución condicional} \label{conditional execution} \index{bifurcación condicional} \index{ejecución condicional} Para escribir programas útiles, casi siempre necesitamos la capacidad de comprobar ciertas condiciones y cambiar el comportamiento del programa en consonancia. Las \textbf{sentencias condicionales} nos dan esta capacidad. La forma más sencilla es la sentencia \texttt{if}: \begin{verbatim} if x > 0: print "x es positivo" \end{verbatim} La expresión booleana tras el \texttt{if} se llama \textbf{condición}. Si es verdadera, entonces la sentencia indentada se ejecuta. Si la condición no es verdadera, no pasa nada. \index{sentencias compuestas} \index{sentencias compuestas!cabecera} \index{sentencias compuestas!cuerpo} \index{sentencias compuestas!bloque de sentencias} \index{sentencias!compuestas} Como otras sentencias compuestas, \texttt{if} consta de una cabecera y un bloque de sentencias: \begin{verbatim} CABECERA: PRIMERA SENTENCIA ... ULITMA SENTENCIA \end{verbatim} La cabecera comienza con una nueva línea y termina con el signo de dos puntos. Los elementos indentados que siguen se llaman \textbf{bloque} de la sentencia. La primera sentencia no indentada marca el fin del bloque. Un bloque de sentencias dentro de una sentencia compuesta recibe el nombre de \textbf{cuerpo} de la sentencia. \index{bloque} \index{sentencias!bloque} \index{cuerpo} No hay límite a la cantidad de sentencias que pueden aparecer en el cuerpo de una sentencia \texttt{if}, pero debe haber al menos una. A veces, es útil tener un cuerpo sin sentencias, (normalmente como reserva de espacio para algo de código que todavía no ha escrito). En tales casos, puede usted utilizar la sentencia {\tt pass}, que no hace nada. \index{sentencia pass} \index{pass!sentencia} \section{Ejecución alternativa} \label{alternative execution} Una segunda forma de la sentencia \texttt{if} es la ejecución alternativa, en la que hay dos posibilidades, y la condición determina cuál de ellas se ejecuta. La sintaxis tiene este aspecto: \begin{verbatim} if x%2 == 0: print x, "es par" else: print x, "es impar" \end{verbatim} Si el resto cuando se divide \texttt{x} entre 2 es cero, entonces sabemos que \texttt{x} es par, y este programa muestra un mensaje a tal efecto. Si la condición es falsa, se ejecuta el segundo lote de sentencias. Puesto que la condición debe ser verdadera o falsa, se ejecutará exactamente una de las alternativas. Llamamos {\bf ramas} a las posibilidades porque son ramas del flujo de ejecución. \index{rama} Como un aparte, si piensa que querrá comprobar con frecuencia la paridad de números, quizá desee {}``envolver'' este código en una función: \begin{verbatim} def imprimeParidad(x): if x%2 == 0: print x, "es par" else: print x, "es impar" \end{verbatim} Ahora tiene una función llamada \texttt{imprimeParidad} que muestra el mensaje apropiado para cada número entero que usted le pase. Llame a esta función de la manera siguiente: \begin{verbatim} >>> imprimeParidad(17) >>> imprimeParidad(y+1) \end{verbatim} \section{Condiciones encadenadas} \index{condiciones encadenadas} \index{condiciones!encadenadas} A veces hay más de dos posibilidades y necesitamos más de dos ramas. Una forma de expresar tal computación es un {\bf conditional encadenado}: \begin{verbatim} if x < y: print x, "es menor que", y elif x > y: print x, "es mayor que", y else: print x, "y", y, "son iguales" \end{verbatim} \texttt{elif} es una abreviatura de \char`\"{}else if\char`\"{}. De nuevo, sólo se ejecutará una rama. No hay límite al número de sentencias \texttt{elif}, pero sólo se permite una sentencia {\tt else} (que puede omitirse) y debe ser la última rama de la sentencia: \begin{verbatim} if eleccion == 'A': funcionA() elif eleccion == 'B': funcionB() elif eleccion == 'C': funcionC() else: print "Eleccion no valida." \end{verbatim} Las condiciones se comprueban en orden. Si la primera es falsa, se comprueba la siguiente, y así. Si una de ellas es cierta, se ejecuta la rama correspondiente y termina la sentencia. Incluso si es cierta más de una condición, sólo se ejecuta la primera rama verdadera. \begin{quote} {\em Como ejercicio, envuelva estos ejemplos en funciones llamadas {\tt compara(x, y)} y {\tt resuelve(eleccion)}.} \end{quote} \section{Condiciones anidadas} Una condición puede estar anidada dentro de otra. Podíamos haber escrito así el ejemplo de tricotomía: \begin{verbatim} ~~if x == y: ~~~~print x, "y", y, "son iguales" ~~else: ~~~~if x < y: ~~~~~~print x, "es menor que", y ~~~~else: ~~~~~~print x, "es mayor que", y \end{verbatim} La condición externa que contiene dos ramas. La primera rama contiene una sentencia simple de salida. La segunda rama contiene otra sentencia \texttt{if}, que tiene dos ramas en sí misma. Estas dos ramas son ambas sentencias de salida de datos, aunque podrían ser igualmente sentencias condicionales. Aunque la indentación de las sentencias hace la estructura evidente, las condiciones anidadas en seguida se vuelven difíciles de leer. En general es una buena idea evitarlas cuando pueda. Los operadores lógicos suelen facilitar un modo de simplificar las sentencias condicionales anidadas. Por ejemplo, podemos reescribir el código siguiente con un sólo condicional: \beforeverb \begin{verbatim} if 0 < x: if x < 10: print "x es un número positivo de un dígito." \end{verbatim} \afterverb % La sentencia {\tt print} sólo se ejecuta si conseguimos superar ambos condicionales, así que podemos usar el operador {\tt and}: \beforeverb \begin{verbatim} if 0 < x and x < 10: print "x es un número positivo de un dígito." \end{verbatim} \afterverb % Estos tipos de condiciones son habituales, por lo que Python nos proporciona una sintaxis alternativa similar a la notación matemática: \beforeverb \begin{verbatim} if 0 < x < 10: print "x es un número positivo de un dígito." \end{verbatim} \afterverb % Esta condición es semánticamente la misma que la expresión booleana compuesta y que el condicional anidado. \section{La sentencia \texttt{return} } \index{sentencia return} \index{sentencia!return} La sentencia \texttt{return} le permite terminar la ejecución de una función antes de alcanzar su final. Una razón para usarla es detectar una condición de error: \begin{verbatim} import math def imprimeLogaritmo(x): if x <= 0: print "Solo numeros positivos, por favor." return result = math.log(x) print "El log de x es", result \end{verbatim} La función \texttt{imprimeLogaritmo} toma un parámetro llamado \texttt{x}. Lo primero que hace es comprobar si \texttt{x} es menor o igual que cero, en cuyo caso muestra un mensaje de error y luego usa \texttt{return} para salir de la función. El flujo de la ejecución vuelve inmediatamente al llamante y no se ejecutan las líneas restantes de la función. Recuerde que para usar una función del módulo math tiene que importarlo. \section{Recursividad} \label{recursion} \index{recursividad} Ya mencionamos que es legal que una función llame a otra, y de ello hemos visto ya varios ejemplos. Olvidamos mencionar que también es legal que una función se llame a sí misma. Puede no resultar evidente por qué es bueno esto, pero viene a resultar una de las cosas más interesantes y curiosas que puede hacer un programa. Examine por ejemplo la siguiente función: \begin{verbatim} def cuenta_atras(n): if n == 0: print "Despegando!" else: print n cuenta_atras(n-1) \end{verbatim} \texttt{cuenta\_atras} espera que su parámetro, {\tt n}, sea un entero positivo. Si {\tt n} el parámetro es cero, muestra la palabra {}``Despegando!''. En otro caso, muestra {\tt n} y luego llama a la función llamada \texttt{cuenta\_atras} (ella misma) pasándole como argumento \texttt{n-1}. ¿Qué sucede si llamamos a la función de la siguiente manera? \begin{verbatim} >>> cuenta_atras(3) \end{verbatim} La ejecución de \texttt{cuenta\_atras} comienza con \texttt{n=3}, y puesto que \texttt{n} no es cero, da como salida el valor 3, y luego se llama a sí misma ... \begin{quote} La ejecución de \texttt{cuenta\_atras} comienza con \texttt{n=2}, y puesto que \texttt{n} no es cero, muestra el valor 2 y luego se llama a sí misma ... \begin{quote} La ejecución de \texttt{cuenta\_atras} comienza con \texttt{n=1}, y puesto que \texttt{n} no es cero, muestra el valor 1, y luego se llama a sí misma... \begin{quote} La ejecución de \texttt{cuenta\_atras} comienza con \texttt{n=0}, y puesto que \texttt{n} es cero, muestra la palabra {}``Despegando!'' y luego retorna. \end{quote} La \texttt{cuenta\_atras} que dio \texttt{n=1} retorna. \end{quote} La \texttt{cuenta\_atras} que dio \texttt{n=2} retorna. \end{quote} La \texttt{cuenta\_atras} que dio \texttt{n=3} retorna. Y entonces ya está de vuelta en \texttt{\_\_main\_\_} (menudo viaje). De manera que la salida completa presenta el siguiente aspecto: \begin{verbatim} 3 2 1 Despegando! \end{verbatim} Como segundo ejemplo, consideremos de nuevo las funciones \texttt{nuevaLinea} and \texttt{tresLineas}. \begin{verbatim} def nuevaLinea(): print def tresLineas(): nuevaLinea() nuevaLinea() nuevaLinea() \end{verbatim} Aunque todas funcionan, no serían de mucha ayuda si quisiera mostrar 2 líneas nuevas o 106. Una mejor alternativa será: \begin{verbatim} def nLineas(n): if n > 0: print nLineas(n-1) \end{verbatim} Este programa es parecido a \texttt{cuenta\_atras}; mientras \texttt{n} sea mayor que cero, muestra una nueva línea, y luego se llama a sí misma para mostrar \texttt{>n-1} nuevas líneas más. De esta manera, el número total de nuevas líneas es \texttt{1 + (n-1)}, que si rescata su álgebra verá que es \texttt{n}. El proceso por el que una función se llama a sí misma se llama \textbf{recursividad}, y dichas funciones se denominan recursivas. \index{recursividad} \index{funciones!recursivas} \section{Diagramas de pila para funciones recursivas} \index{diagramas de pila} \index{marco de función} \index{marco} El la Sección~\ref{stackdiagram} utilizamos un diagrama de pila para representar el estado de un programa durante la llamada de una función. El mismo tipo de diagrama puede hacer más fácil interpretar una función recursiva. Cada vez que se llama a una función, Python crea un nuevo marco para la función, que contiene sus variables locales y parámetros. En el caso de una función recursiva, puede haber más de un marco en la pila al mismo tiempo. La figura muestra un diagrama de pila para \texttt{cuenta\_atras}, invocada con \texttt{n = 3}: \vspace{0.1in} \centerline{\includegraphics{../illustrations/stack2.eps} } \vspace{0.1in} Como es habitual, en lo alto de la pila está el marco de \texttt{\_\_main\_\_}. Está vacía porque no hemos ninguna variable sobre \texttt{\_\_main\_\_} ni le hemos pasado ningún parámetro. Los cuatro marcos de \texttt{cuenta\_atras} tienen valores diferentes para el parámetro \texttt{n}. El fondo de la pila, donde \texttt{n=0}, se llama {\bf caso base}. No hace una llamada recursiva, de manera que no hay más marcos. \begin{quote} \emph{Como actividad, dibuje un diagrama de pila para} \texttt{nLineas}\emph{, invocada con el parámetro} \texttt{n=4}. \end{quote} \index{caso base} \index{recursividad!caso base} \section{Recursividad infinita} \index{recursividad!infinita} \index{recursividad infinita} \index{error en tiempo de ejecución} \index{error!en tiempo de ejecución} \index{traza inversa} Si una recursión no alcanza nunca el caso base, seguirá haciendo llamadas recursivas para siempre y nunca terminará. Esta circunstancia se conoce como \textbf{recursividad infinita}, y generalmente no se la considera una buena idea. Este es un programa mínimo con recursividad infinita: \beforeverb \begin{verbatim} def recurre(): recurre() \end{verbatim} \afterverb % El la mayoría de los entornos de programación, un programa con recursividad infinita no se ejecutará realmente para siempre. Python informará de un mensaje de error cuando se alcance el nivel máximo de recursividad: \beforeverb \begin{verbatim} File "", line 2, in recurse (98 repetitions omitted) File "", line 2, in recurse RuntimeError: Maximum recursion depth exceeded \end{verbatim} \afterverb Esta traza inversa es un poco mayor que la que vimos en el capítulo anterior. ¡Cuando sucede el error, hay 100 marcos {\tt recurre} en la pila! \begin{quote} \emph{Como actividad, escriba una función con recursividad infinita y ejecútela en el intérprete de Python.} \end{quote} \section{Entrada por teclado} Los programas que hemos escrito hasta ahora son un poco maleducados en el sentido de que no aceptan entradas de datos del usuario. Simplemente hacen lo mismo siempre. Python proporciona funciones internas que obtienen entradas desde el teclado. La más sencilla se llama \texttt{raw\_input}. Cuando llamamos a esta función, el programa se detiene y espera a que el usuario escriba algo. Cuando el usuario pulsa la tecla \texttt{Return} o \texttt{Enter}, el programa se reanuda y \texttt{raw\_input} devuelve lo que el usuario escribió como tipo \texttt{string}: \begin{verbatim} >>> entrada = raw_input () A qué estás esperando? >>> print entrada A qué estás esperando? \end{verbatim} Antes de llamar a \texttt{raw\_input} es conveniente mostrar un mensaje que le pida al usuario el dato solicitado. Este mensaje se llama {\bf indicador} (prompt en inglés). Puede proporcionarle un indicador a \texttt{raw\_input} como argumento: \index{indicador} \begin{verbatim} >>> nombre = raw_input ("Cómo te llamas? ") Cómo te llamas? Héctor, héroe de los Troyanos! >>> print nombre Héctor, héroe de los Troyanos! \end{verbatim} Si espera que la entrada sea un entero, utilice la función \texttt{input}. Por ejemplo: \begin{verbatim} >>> indicador = \ ... "Cuál es la velocidad de una golondrina sin carga?\n" >>> velocidad = input (indicador) \end{verbatim} Si el usuario teclea una cadena de números, se convertirá en un entero y se asignará a \texttt{velocidad}. Por desgracia, si el usuario escribe algo que no sea un dígito, el programa dará un error: \begin{verbatim} >>> velocidad = input (indicador) Cuál es la velocidad de una golondrina sin carga? Se refiere usted a la golondrina europea o a la africana? SyntaxError: invalid syntax \end{verbatim} Para evitar este tipo de error, generalmente es buena idea usar \texttt{raw\_input} para obtener una cadena y usar entonces las funciones de conversión para convertir a otros tipos. \section{Glosario} \begin{description} \item [operador~módulo:] Operador, señalado con un signo de tanto por ciento (\texttt{\%}), que trabaja sobre enteros y devuelve el resto cuando un número se divide entre otro. \item[expresión booleana:] Una exprersión que es cierta o falsa. \item[operador de comparación:] Uno de los operadores que comparan dos valores: {\tt ==}, {\tt !=}, \verb|>|, \verb|<|, {\tt >=} y {\tt <=}. \item[operador lógico:] Uno de los operadores que combinan expresiones booleanas: {\tt and}, {\tt or} y {\tt not}. \item [sentencia~condicional:] Sentencia que controla el flujo de ejecución de un programa dependiendo de cierta condición. \item [condición:] La expresión booleana de una sentencia condicional que determina qué rama se ejecutará. \item [sentencia~compuesta:] Estructura de Python que está formado por una cabecera y un cuerpo. La cabecera termina en dos puntos (:). El cuerpo tiene una sangría con respecto a la cabecera. \item [bloque:] Grupo sentencias consecutivas con el mismo sangrado. \item [cuerpo:] En una sentencia compuesta, el bloque de sentencias que sigue a la cabecera de la sentencia. \item [anidamiento:] Una estructura de programa dentro de otra; por ejemplo, una sentencia condidional dentro de una o ambas ramas de otra sentencia condicional. \item [recursividad:] El proceso de volver a llamar a la función que se está ejecutando en ese momento. \item [caso~base:] En una función recursiva, la rama de una sentencia condicional que no ocasiona una llamada recursiva. \item [recursividad~infinita:] Función que se llama a sí misma recursivamente sin alcanzar nunca el caso base. A la larga una recursión infinita provocará un error en tiempo de ejecución. \item [indicador:] indicador visual que invita al usuario a introducir datos. \index{operador módulo} \index{expresión booleana} \index{expresión!booleana} \index{sentencia condicional} \index{sentencia!condicional} \index{condición} \index{sentencia compuesta} \index{rama} \index{cuerpo} \index{bloque} \index{anidamiento} \index{recursividad} \index{caso base} \index{recursividad infinita} \index{indicador} \end{description} %\end{document}


Next Up Previous Hi Index