6. Iteración

6.1. Asignación múltiple

Puede que ya haya descubierto que es permitido realizar más de una asignación a la misma variable. Una nueva asignación hace que la variable existente se refiera a un nuevo valor (y deje de referirse al viejo valor).

bruno = 5
print bruno,
bruno = 7
print bruno

La salida de este programa es 5 7, ya que la primera vez que se muestra bruno su valor es 5, y la segunda vez su valor es 7. La coma al final de la primera sentencia print impide que se muestre una nueva línea en ese punto, por eso ambos valores aparecen en la misma línea.

He aquí el aspecto de una asignación múltiple en un diagrama de estado:

Con la asignación múltiple es muy importante distinguir entre una operación de asignación y una sentencia de igualdad. Puesto que Python usa el signo igual (=) para la asignación, es tentador interpretar una sentencia como a = b como una sentencia de igualdad. ¡Pero no lo es!

Para empezar, la igualdad es simétrica y la asignación no lo es. Por ejemplo, en matemáticas, si a = 7 entonces 7 = a. Pero en Python la sentencia a = 7 es válida, y 7 = a no lo es.

Además, en matemáticas, una sentencia de igualdad es verdadera siempre. Si a = b ahora, entonces a siempre será igual a b. En Python, una sentencia de asignación puede hacer que dos variables sean iguales, pero no tienen por qué quedarse así:

a = 5
b = a    # a y b son ahora iguales
a = 3    # a y b ya no son iguales

La tercera línea cambia el valor de a pero no cambia el valor de b, por lo tanto ya dejan de ser iguales. (En algunos lenguajes de programación se usa un símbolo distinto para la asignación, como <- o :=, para evitar confusión.)

6.2. Actualización de variables

Una de las formas más comunes de asignación múltiple es la actualización, donde el nuevo valor de la variable depende del anterior.

x = x + 1

Esto significa “tome el valor actual de x, súmele uno, y después actualice x con el nuevo valor.”

Si intenta actualizar una variable que no existe obtendrá un error, puesto que Python evalúa la expresión del lado derecho del operador de asignación antes de asignar el valor resultante al nombre del lado izquierdo:

>>> x = x + 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined

Antes de actualizar una variable tiene que inicializarla, usualmente con una asignación simple:

>>> x = 0
>>> x = x + 1
>>>

Actualizar una variable sumándole 1 de denomina un incremento; y restándole 1 de llama un decremento.

6.3. La sentencia while

Las computadoras se usan a menudo para automatizar tareas repetitivas. Realizar repetidamente tareas idénticas o similares sin cometer errores es algo que las computadoras hacen bien y que los seres humanos hacemos limitadamente.

La ejecución repetida de un conjunto de sentencias se llama iteración. Por ser la iteración tan común, el lenguaje Python proporciona varias características que la hacen más fácil. La primera característica que vamos a considerar es la sentencia while.

Esta es una función llamada cuenta_atras que muestra el uso de la sentencia while:

def cuenta_atras(n):
    while n > 0:
        print n
        n = n-1
    print "¡Despegue!"

Casi podría leer la sentencia while como en lenguaje natural. La función anterior significa: “Mientras n sea mayor que 0, continúe mostrando el valor de n y luego reduciendo el valor de n en 1. Cuando llegue a 0, muestre la palabra ¡Despegue!

Más formalmente, el flujo de ejecución de una sentencia while es el siguiente:

  1. Evalúa la condición, devolviendo False o True.
  2. Si la condición es falsa, se sale de la sentencia while y continúa la ejecución con la siguiente sentencia.
  3. Si la condición es verdadera, ejecuta cada una de las sentencias en el cuerpo y regresa al paso 1.

El cuerpo consiste de todas las sentencias con el mismo sangrado bajo el encabezado.

Este tipo de flujo de llama bucle porque el tercer paso del bucle vuelve arriba. Nótese que si la condición es falsa la primera vez que se pasa el bucle, las sentencias del interior del bucle no se ejecutan nunca.

El cuerpo del bucle debería cambiar el valor de una o más variables de manera que, en algún momento, la condición sea falsa y el bucle termine. En caso contrario, el bucle se repetirá indefinidamente, que es lo que se llama bucle infinito. Una interminable fuente de diversión para los informáticos es la observación de que las instrucciones del champú “Enjabone, enjuague, repita”, son un bucle infinito.

En el caso de cuenta_atras, podemos probar que el bucle termina porque sabemos que el valor de n es finito, y podemos ver que el valor de n se hace más pequeño cada vez que pasa por el bucle, así en cierto momento llegaremos a 0. En otros casos no es tan fácil decirlo:

def secuencia(n):
    while n != 1:
        print n,
        if n % 2 == 0:        # n es par
            n = n / 2
        else:                 # n es non
            n = n * 3 + 1

La condición de este bucle es n != 1, de manera que el bucle continuará hasta que n sea 1, que hará que la condición sea falsa.

Cada vez que pasa por el bucle, el programa muestra como salida el valor de n y luego comprueba si es par o non. Si es par, el valor de n se divide entre dos. Si es non, el valor se sustituye por n * 3 + 1. Por ejemplo, si el valor de inicio (el argumento pasado a la función secuencia) es 3, la secuencia resultante es 3, 10, 5, 16, 8, 4, 2, 1.

Puesto que n a veces aumenta y a veces disminuye, no hay una prueba obvia de que n alcanzará alguna vez el valor 1, o de que el programa vaya a terminar. Para algunos valores particulares de n, podemos probar la terminación. Por ejemplo, si el valor de inicio es una potencia de dos, entonces el valor de n será par cada vez que se pasa por el bucle, hasta que llegue a 1. El ejemplo anterior produce dicha secuencia si se inicia con 16.

Independientemente de los valores particulares, la pregunta interesante es si podemos probar que este programa termina para todos los valores de n. Hasta ahora, nadie ha sido capaz de probar que lo hace o ¡que no lo hace!

6.4. Rastreando un programa

Para escribir programas de cómputo efectivos, un programador necesita desarrollar la habilidad de rastrear la ejecución de un programa. El rastreo implica “convertirse en la computadora” y seguir el flujo de ejecución mediante una corrida de prueba del programa, registrando el estado de todas las variables y de cualquier salida que el programa genere después de la ejecución de cada instrucción.

Para comprender este proceso rastreemos la llamada a secuencia(3) de la sección anterior. Al inicio del rastreo tenemos una variable local, n (el parámetro), con un valor inicial de 3. Dado que 3 no es igual a 1, el cuerpo del bucle while se ejecuta. Luego se muestra el número 3 y se evalúa la instrucción 3 % 2 == 0. Puesto que el resultado de la evaluación es False, la rama else se ejecuta y 3 * 3 + 1 es evaluado y asignado a n.

Para darle seguimiento a todo mediante el rastreo manual de un programa, en una hoja de papel escriba el encabezado de una columna para cada variable creada conforme el programa se ejecuta y otro para la salida. Ahora nuestro rastreo se vería como esto:

n              salida
---            ------
3              3
10

Puesto que 10 != 1 el resultado de la evaluación es True, el cuerpo del bucle se ejecuta de nuevo, y el número 10 es mostrado. El resultado de evaluar 10 % 2 == 0 es verdadero, entonces la rama if se ejecuta y n toma el valor de 5. Al final del rastreo tenemos:

n              salida
---            ------
3              3
10             10
5              5
16             16
8              8
4              4
2              2
1

El rastreo puede ser un tanto tedioso y propenso al error (¡es por eso que de inicio usamos las computadoras para hacer estas cosas!), pero es una habilidad esencial que el programador debe tener. De este rastreo podemos aprender mucho sobre cómo funciona el código. Por ejemplo, podemos observar que tan pronto como n se convierte en una potencia de base 2, el programa requiere log2(n) ejecuciones del cuerpo del bucle para completarse. También podemos observar que el número 1 al final del proceso no se mostrará en la salida.

6.5. Contando dígitos

La siguiente función cuenta el número de dígitos en un entero positivo expresado en formato decimal:

def num_digitos(n):
    contador = 0
    while n:
        contador = contador + 1
        n = n / 10
    return contador

Una llamada a num_digitos(710) regresará 3. Rastree la ejecución de esta llamada a función para que se convenza de que funciona.

Esta función muestra otro patrón de cálculo llamado contador. La variable contador se inicializa a 0 y luego se incrementa cada vez que el cuerpo del bucle es ejecutado. Cuando el bucle termina, la variable contador contiene el resultado —el número total de veces que el cuerpo del bucle fue ejecutado, que es el mismo que el número de dígitos.

Si solamente deseamos contar los dígitos que son 0 o 5, agregar una sentencia condicional antes de incrementar el contador hará el truco:

def digitos_cero_y_cinco(n):
    contador = 0
    while n:
        digito = n % 10
        if digito == 0 or digito == 5:
            contador = contador + 1
        n = n / 10
    return contador

Confirme que digitos_cero_y_cinco(1055030250) devuelve 7.

6.6. Asignación abreviada

Incrementar una variable es tan común que Python provee una sintaxis abreviada para ello:

>>> contador = 0
>>> contador += 1
>>> contador
1
>>> contador += 1
>>> contador
2
>>>

contador += 1 es una abreviación de contador = contador + 1. El valor del incremento no tiene que ser 1 necesariamente:

>>> n = 2
>>> n += 5
>>> n
7
>>>

También existen las abreviaciones *=, -=, /=, y %=:

>>> n = 2
>>> n *= 5
>>> n
10
>>> n -= 4
>>> n
6
>>> n /= 2
>>> n
3
>>> n %= 2
>>> n
1

6.7. Tablas

Una de las cosas para las que los bucles son buenos es la generación de datos tabulares. Antes de que las computadoras se consiguieran fácilmente, la gente tenía que calcular a mano logaritmos, senos, cosenos y otras funciones matemáticas. Para facilitarlo, los libros de matemáticas contenían largas tablas que listaban los valores de estas funciones. Generar estas tablas era una tarea lenta y aburrida, y tendían quedar llenas de errores.

Con la aparición de las computadoras, una de las reacciones iniciales fue “¡Qué bueno! Podemos usar las computadoras para generar tablas, así no tendrán errores.” Resultó cierto (casi), pero no se vio más allá. Poco después las computadoras y las calculadoras estaban tan extendidas por todas partes que las tablas se volvieron obsoletas.

Bueno, casi. Para algunas operaciones las computadoras usan tablas de valores para obtener una respuesta aproximada y luego realizan más cálculos para mejorar la aproximación. En algunos casos ha habido errores en las tablas subyacentes; el más famoso estaba en la tabla que el Pentium de Intel usaba para llevar a cabo la división de punto flotante.

Aunque una tabla logarítmica ya no es tan útil como antes, todavía constituye un buen ejemplo de iteración. El siguiente programa muestra una secuencia de valores en la columna izquierda y 2 elevado a cada uno de esos valores en la columna derecha:

x = 1
while x < 13:
    print x, '\t', 2**x
    x += 1

La cadena '\t' representa un carácter de tabulación. La diagonal invertida en '\t' indica el comienzo de una secuencia de escape. Las secuencias de escape se usan para representar caracteres no visibles como las tabulaciones y nuevas líneas. La secuencia \n representa una nueva línea.

Una secuencia de escape puede aparecer en cualquier posición de una cadena; en este ejemplo, la secuencia de escape de tabulación es toda la cadena. ¿Cómo cree que puede representar una diagonal invertida en una cadena?

A medida que los caracteres y las cadenas se muestran en la pantalla, un marcador invisible llamado cursor lleva la cuenta de donde irá el siguiente carácter. Tras la ejecución de una sentencia print, el cursor va normalmente al inicio de la siguiente línea.

El carácter de tabulación hace que el cursor se desplace a la derecha hasta que alcance una de los marcas de tabulación. Los caracteres de tabulación son útiles para alinear columnas de texto, como en la salida del programa anterior:

1       2
2       4
3       8
4       16
5       32
6       64
7       128
8       256
9       512
10      1024
11      2048
12      4096

Debido a los caracteres de tabulación entre las columnas, la posición de la segunda columna no depende del número de dígitos de la primera.

6.8. Tablas de dos dimensiones

Una tabla de dos dimensiones es una tabla en la que usted elige una fila y una columna y lee el valor de la intersección. Una tabla de multiplicar es un buen ejemplo. Supongamos que desea mostrar una tabla de multiplicar para los valores del 1 al 6.

Una buena manera de comenzar es escribir un bucle que muestre los múltiplos de 2, todos en una línea:

i = 1
while i <= 6:
    print 2 * i, '   ',
    i += 1
print

La primera línea inicializa una variable llamada i, que actúa como contador o variable de bucle. Conforme se ejecuta el bucle, el valor de i se incrementa de 1 a 6. Cuando i vale 7, el bucle termina. Cada vez que se pasa por el bucle, se muestra el valor 2 * i seguido por tres espacios.

De nuevo, la coma de la sentencia print suprime la generación de una nueva línea. Después de completar el bucle, la segunda sentencia print crea una nueva línea.

La salida del programa es:

2      4      6      8      10     12

Todo bien hasta ahora. El siguiente paso es encapsular y generalizar.

6.9. Encapsulamiento y generalización

El encapsulamiento es el proceso de envolver segmentos de código en una función, permitiéndole aprovechar todos los beneficios de las funciones. Usted ya ha visto dos ejemplos de encapsulamiento: muestra_paridad en el capítulo 4; y es_divisible en el capítulo 5.

La generalización es tomar algo específico, tal como mostrar los múltiplos de 2, y convertirlo en algo más general, tal como mostrar los múltiplos de cualquier entero.

Esta función encapsula el bucle anterior y lo generaliza para mostrar múltiplos de n:

def muestra_multiplos(n):
    i = 1
    while i <= 6:
        print n * i, '\t',
        i += 1
    print

Para encapsular, todo lo que tuvimos que hacer fue agregar la primera línea, que declara el nombre de la función y la lista de parámetros. Para generalizar, todo lo que tuvimos que hacer fue reemplazar el número 2 por el parámetro n.

Si hacemos una llamada a esta función con el número 2 como argumento obtenemos, como antes, la misma salida. Con el número 3 como argumento la salida es:

3      6      9      12     15     18

Con el número 4 como argumento, la salida es:

4      8      12     16     20     24

A estas alturas es probable que haya adivinado cómo mostrar una tabla de multiplicación — llamando a la función muestra_multiplos repetidamente con diferentes argumentos. De hecho, podemos usar otro bucle:

i = 1
while i <= 6:
    muestra_multiplos(i)
    i += 1

Observe lo similar de este bucle con el bucle del interior de la función muestra_multiplos. Todo lo que hicimos fue reemplazar la sentencia print por una llamada a función.

La salida de este programa es una tabla de multiplicación:

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

6.10. Más encapsulamiento

Para demostrar de nuevo el encapsulamiento tomemos el código de la última sección y envolvámoslo en una función:

def muestra_tabla_de_mult():
    i = 1
    while i <= 6:
        muestra_multiplos(i)
        i += 1

Este proceso es un plan de desarrollo común. Desarrollamos código escribiendo líneas de código fuera de cualquier función, o lo escribimos directamente en el intérprete de Python. Cuando logramos que el código se ejecute correctamente, lo tomamos y lo envolvemos en una función.

Este plan de desarrollo es particularmente útil si, cuando empieza a escribir código, no sabe cómo dividir el programa en funciones. Este método le deja diseñar el programa conforme va avanzando en su codificación.

6.11. Variables locales

Quizá se esté preguntando cómo podemos usar la misma variable, i, en las funciones muestra_multiplos y muestra_tabla_de_mult. ¿No se causa algún problema cuando una de las funciones cambia el valor de la variable?

La respuesta es no, porque la i en muestra_multiplos `` y la ``i en muestra_tabla_de_mult no son la misma variable.

Las variables creadas dentro de una definición de función son locales; usted no puede acceder a una variable local desde fuera de su función “huésped”. Esto quiere decir que está en libertad de tener múltiples variables con el mismo nombre, siempre y cuando no estén en la misma función.

El diagrama de pila de este programa muestra que las dos variables llamadas i no son la misma variable. Pueden referirse a valores diferentes, y el cambio de una no afecta a la otra.

El valor de i en muestra_tabla_de_mult va de 1 a 6. En el diagrama está en 3. La siguiente vuelta en el bucle será 4. Cada vuelta en el bucle muestra_tabla_de_mult llama a muestra_multiplos con el valor actual de i como argumento. Este valor se asigna al parámetro n.

Dentro de muestra_multiplos el valor de i va de 1 a 6. En el diagrama está en 2. El cambio en este variable no tiene efecto alguno en el valor de i en muestra_tabla_de_mult.

Es común y perfectamente válido tener diferentes variables locales con el mismo nombre. En particular, nombres como i y j son frecuentemente usados como variables de bucles. Si evita usarlas en una función sólo porque ya las usó en otra, probablemente hará un programa difícil de leer.

6.12. Más generalización

Como un ejemplo más de generalización imagine que requiere un programa que muestre una tabla de multiplicación de cualquier tamaño, no solamente una tabla de seis por seis. Podría agregar un parámetro a muestra_tabla_de_mult:

def muestra_tabla_de_mult(mayor):
    i = 1
    while i <= mayor:
        muestra_multiplos(i)

        i += 1

Sustituimos el valor 6 por el parámetro mayor. Si llamamos a muestra_tabla_de_mult con el argumento 7, ésta muestra:

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      14     21     28     35     42

Esto está bien, excepto que probablemente queremos que la tabla sea cuadrada — con el mismo número de renglones y columnas. Para hacer esto, agregamos otro parámetro a muestra_multiplos para especificar el número de columnas que tendría la tabla.

Sólo para molestar llamamos a este parámetro mayor, demostrando que diferentes funciones pueden tener parámetros con el mismo nombre (precisamente como las variables locales). Aquí está el programa completo:

def muestra_multiplos(n, mayor):
    i = 1
    while i <= mayor:
        print n * i, '\t',
        i += 1
    print

def muestra_tabla_de_mult(mayor):
    i = 1
    while i <= mayor:
        muestra_multiplos(i, mayor)
        i += 1

Note que cuando agregamos un nuevo parámetro, tuvimos que cambiar la primera línea de la función (el encabezado de la función), y también el lugar desde donde la función es llamada en muestra_tabla_de_mult.

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 generaliza apropiadamente una función, a menudo obtiene un programa con capacidades que no planeó. Por ejemplo, puede notar que, puesto que ab = ba, todas las entradas en la tabla aparecen dos veces. Puede ahorrar tinta imprimiendo solamente media tabla. Para hacerlo, solamente tiene que cambiar una línea de muestra_tabla_de_mult. Cambie

muestra_multiplos(i, mayor)

a

muestra_multiplos(i, i)

y obtiene:

1
2      4
3      6      9
4      8      12     16
5      10     15     20     25
6      12     18     24     30     36
7      14     21     28     35     42     49

6.13. Funciones

Hasta el momento hemos mencionado en alguna ocasión “todas las cosas para las que sirven las funciones.” Ahora puede que se pregunte qué son exactamente esas cosas. He aquí algunas de ellas:

  1. Dar un nombre a una secuencia de sentencias hace que su programa sea más fácil de leer y depurar.
  2. Dividir en funciones un programa extenso le permite separar las partes del programa, depurarlas aisladamente, y luego componerlas en un todo.
  3. Las funciones facilitan el uso de la iteración.
  4. Las funciones bien diseñadas son generalmente útiles para muchos programas. Una vez que escribe y depura una, puede reutilizarla.

6.14. Método de Newton

Los bucles son comúnmente utilizados en programas que calculan resultados numéricos, que comienzan con una respuesta aproximada, y que es iterativamente mejorada.

Por ejemplo, una forma de calcular la raíz cuadrada es el método de Newton. Suponga que quiere conocer la raíz cuadrada de n. Si inicia con casi cualquier aproximación, puede calcular una mejor aproximación con la siguiente fórmula:

mejor_aprox = (aprox + n/aprox)/2

Mediante la aplicación repetida de esta fórmula, hasta que la mejor aproximación sea igual a la previa, podemos escribir una función para el cálculo de la raíz cuadrada:

def raizcuad(n):
    aprox = n/2.0
    mejor_aprox = (aprox + n/aprox)/2.0
    while mejor_aprox != aprox:
        aprox = mejor_aprox
        mejor_aprox = (aprox + n/aprox)/2.0
    return aprox

Intente hacer una llamada a esta función con el número 25 como argumento para confirmar que devuelve 5.0.

6.15. Algoritmos

El método de Newton es un ejemplo de un algoritmo: éste es un proceso mecánico para resolver una categoría de problemas (en este caso, el cálculo de raíces cuadradas).

No es fácil definir un algoritmo. Podría ayudar empezar con algo que no es un algoritmo. Cuando aprendió a multiplicar números dígitos probablemente memorizó la tabla de multiplicación. En efecto, memorizó 100 soluciones específicas. Este tipo de conocimiento no es un algoritmo.

Pero si usted fuera “flojo,” probablemente haría trampa aprendiendo algunos trucos. Por ejemplo, para encontrar el producto de n y 9, puede escribir n - 1 como el primer dígito y 10 - n como el segundo dígito. Este truco es una solución general para la multiplicación de cualquier número dígito por 9. ¡Éste es un algoritmo!

De manera similar, las técnicas que aprendió para la suma con “llevamos tanto”, la resta con “pedimos prestado tanto”, y la división “larga o con galera o de casita” son todas ellas algoritmos. Una de las características de los algoritmos es que no requieren inteligencia alguna para realizarlos. Son procesos mecánicos en los cuales cada paso sigue al previo de acuerdo con un conjunto de reglas simples.

En nuestra opinión, es embarazoso que los humanos empleen tanto tiempo en la escuela aprendiendo a utilizar algoritmos que, literalmente, no requieren inteligencia.

Por otro lado, el proceso de diseño de algoritmos es interesante, intelectualmente desafiante, y una parte central de lo que llamamos programación.

Algunas de las cosas que la gente hace naturalmente, sin dificultad o conscientemente, son las más difíciles de expresar en algoritmos. El entendimiento del lenguaje natural es un buen ejemplo. Todos lo hacemos, pero hasta ahora nadie ha sido capaz de explicar cómo lo hacemos, no al menos en forma de un algoritmo.

6.16. Glosario

asignación múltiple:
Hacer más de una asignación a la misma variable durante la ejecución de un programa.
inicialización (de una variable):
Inicializar una variable es darle un valor inicial, usualmente en el contexto de la asignación múltiple. Dado que en Python las variables no existen hasta que se les asignan valores, ellas son inicializadas cuando son creadas. En otros lenguajes de programación no es así, y las variables se pueden crear sin ser inicializadas, en cuyo caso pueden contener valores por defecto o basura.
incremento
Tanto como sustantivo como verbo, incremento significa aumentar en 1.
decremento
Disminuir en 1.
iteración:
Ejecución repetida de un conjunto de sentencias de programación.
bucle:
Sentencia o grupo de sentencias que se ejecutan repetidamente hasta que se cumple una condición de terminación.
bucle infinito:
Bucle cuya condición de terminación nunca se cumple.
rastreo:
Seguir a mano el flujo de ejecución de un programa, anotando los cambios de estado de las variables y cualquier salida producida.
contador
Una variable que se utiliza para contar algo, usualmente inicializada a cero e incrementada en el cuerpo de un bucle.
cuerpo:
Las sentencias dentro de un bucle.
variable del bucle:
Variable usada como parte de la condición de terminación de un bucle.
tabulación:
Carácter especial que provoca que el cursor se mueva a la siguiente marca de tabulación en la línea actual.
nueva línea:
Carácter especial que provoca que el cursor se mueva al inicio de la siguiente línea.
cursor:
Marcador invisible que sigue el rastro de dónde se mostrará el siguiente carácter.
secuencia de escape:
Carácter de escape, , seguido por uno o más caracteres visibles, que se usan para designar un carácter no imprimible.
encapsulamiento:
Dividir un programa extenso y complejo en componentes (como las funciones) y aislar los componentes unos de otros (usando variables locales, por ejemplo).
generalización:
Sustituir algo innecesariamente específico (como un valor constante) con algo convenientemente general (como una variable o parámetro). La generalización hace el código más versátil, más apto para reutilizarse, y algunas veces, incluso, más fácil de escribir.
plan de desarrollo:
Proceso para desarrollar un programa. En este capítulo, demostramos un estilo de desarrollo basado en generar código para hacer cosas simples y específicas, y luego encapsularlas y generalizarlas.
algoritmo:
Proceso paso a paso para resolver una categoría de problemas.

6.17. Ejercicios

  1. Escriba una cadena sencilla que:

    produzca
    esta
    salida.
  2. Agregue una sentencia print a la función raizcuad definida en la sección 6.14, que muestre el valor de mejor_aprox cada vez que ésta es calculada. Haga una llamada a su función modificada con 25 como argumento y guarde los resultados.

  3. Rastree la ejecución de la última versión de muestra_tabla_de_mult y entienda cómo funciona.

  4. Escriba una función muestra_numeros_triangulares(n) que muestre los primeros n números triangulares. Una llamada a muestra_numeros_triangulares(5) produciría la siguiente salida:

    1       1
    2       3
    3       6
    4       10
    5       15

    (pista: busque en Internet qué es un número triangular.)

  5. Cree un archivo llamado ch06.py y agregue lo siguiente:

    if __name__ == '__main__':
        import doctest
        doctest.testmod()
    

    Escriba una función, es_primo, que tome un solo argumento entero y devuelva True cuando el argumento es un número primo y False en caso contrario. Agregue el código para la prueba unitaria conforme escribe la función.

  6. ¿Qué devolverá num_digitos(0)? Modifíquela para que devuelva 1 en este caso. ¿Por qué una llamada a num_digitos(-24) produce un bucle infinito? (pista: -1/10 da como resultado -1) Modifique num_digitos para que funcione correctamente con cualquier valor entero. Agregue lo siguiente al archivo ch06.py que creó en el ejercicio anterior:

    def num_digitos(n):
        """
          >>> num_digitos(12345)
          5
          >>> num_digitos(0)
          1
          >>> num_digitos(-12345)
          5
        """
    

    Agregue el cuerpo de su función a num_digitos y confirme que pasa la prueba unitaria.

  7. Agregue lo siguiente al archivo ch06.py:

    def num_digitos_pares(n):
        """
          >>> num_digitos_pares(123456)
          3
          >>> num_digitos_pares(2468)
          4
          >>> num_digitos_pares(1357)
          0
          >>> num_digitos_pares(2)
          1
          >>> num_digitos_pares(20)
          2
        """
    

    Escriba el cuerpo para num_digitos_pares para que funcione como se espera.

  8. Agregue lo siguiente al archivo ch06.py:

    def muestra_digitos(n):
        """
          >>> muestra_digitos(13789)
          9 8 7 3 1
          >>> muestra_digitos(39874613)
          3 1 6 4 7 8 9 3
          >>> muestra_digitos(213141)
          1 4 1 3 1 2
        """
    

    Escriba el código para muestra_digitos para que pase el código de la prueba unitaria.

  9. Escriba una función llamada suma_de_cuadrados_de_digitos que calcule la suma de los cuadrados de los dígitos de un entero que se pase como argumento. Por ejemplo, suma_de_cuadrados_de_digitos(987) devolvería 194, puesto que 9**2 + 8**2 + 7**2 == 81 + 64 + 49 == 194.

    def suma_de_cuadrados_de_digitos(n):
        """
          >>> suma_de_cuadrados_de_digitos(1)
          1
          >>> suma_de_cuadrados_de_digitos(9)
          81
          >>> suma_de_cuadrados_de_digitos(11)
          2
          >>> suma_de_cuadrados_de_digitos(121)
          6
          >>> suma_de_cuadrados_de_digitos(987)
          194
        """
    

    Verifique su respuesta contra el código para la prueba unitaria expresado arriba.