-
-
La palabra iteración significa repetición, reiteración; por tanto, en este apartado vamos a estudiar las estructuras que implican repetir o iterar un fragmento de código y las sentencias que se definen en lenguaje C para ello.
Aunque aún no hayamos hablado de este tipo de estructuras, su necesidad resulta casi evidente en múltiples algoritmos. De hecho son las estructuras más importantes en los lenguajes imperativos, como el lenguaje C que estamos estudiando.
Imaginemos por ejemplo que queremos saber si un número natural n es primo. La solución de esta pregunta, según la definición de número primo, implica comprobar si la división de n por los números menores que él, es exacta o no (es decir, si el resto del cociente es cero o no). Por tanto, el código que resolvería esta pregunta implica la necesidad de una estructura iterativa o reiterativa que vaya repitiendo la división desde 2 hasta n-1. Si se quiere refinar el algoritmo para evitar divisiones innecesarias, en realidad sólo sería necesario comprobar el resto de la división por 2 y por los números impares desde 3 hasta el número impar inmediatamente inferior que la raíz cuadrada de n. Aún más fino, sería tener una lista de números primos menores que la raíz cuadrada de n, y comprobar solamente los cocientes entre n y tales números primos.
Otros casos que nos exigen disponer de estructuras iterativas son, por ejemplo aquellos donde un problema matemático se resuelve repitiendo una misma operación. En general, tal es el caso de las expresiones matemáticas que se escriben con un sumatorio. Así, el cálculo de la media aritmética de un conjunto de números puede resolverse almacenando la suma de los números en alguna variable, y finalmente dividir por la cantidad de números que componen la lista:
Otro ejemplo más sofisticado puede ser calcular el valor aproximado de la integral de cierta función f(x) entre dos límites a y b. Para ello, podemos usar la semejanza con la superficie formada por rectángulos que quedan por debajo de la función. Entonces, se puede iterar acumulando la suma de los pequeños rectángulos de altura f(x) y base Dx, desde a hasta b
En el campo del tratamiento y gestión de datos, es evidente que los procesos repetitivos son necesarios. Imaginemos, por ejemplo, la gestión de las cartillas de ahorro de un banco, en donde, en general los procesos realizados consisten en repetir la misma operación para todos sus clientes (por ejemplo calcular los intereses a final de mes, restar las comisiones de mantenimiento, etc.). Análogamente, hoy en día son muy frecuentes los algoritmos iterativos en las llamadas aplicaciones "multimedia", como las que se realizan con imágenes y sonidos. Pongamos por caso un algoritmo que modifique el tono de color en cada uno de los puntos de una imagen (denominados "pixels"). Es obvio que tal algoritmo recorrerá cada uno de los puntos de la imagen, repitiendo la misma operación sobre ellos, es decir, usando algún tipo de estructura iterativa. Y así hasta un sinfín de cálculos matemáticos y algoritmos que se pueden escribir fácilmente usando una estructura iterativa.
De hecho, las estructuras iterativas son tan comunes en los programas escritos en un lenguaje imperativo, que se las suele denominar con el más sencillo nombre de "bucles", en referencia a las vueltas que los bucles o rizos del cabello suelen dar. Por tal motivo, a partir de ahora nos referiremos a las mismas con ese nombre. Además, los bucles o estructuras iterativas no sólo son necesarios en casos donde se haya que realizar un proceso iterativo, sino que nos permiten escribir de forma más cómoda y concisa trozos de código que resultarían largos y pesados de otra forma. Por ejemplo, sería pesado y largo escribir:
x=a*a*a*a*a*a*a*a;Mientras que un buen programador acaba por acostumbrarse a escribir de forma más simple algo como (en pseudocódigo):
x=1.0; expon=8;
repite expon veces
x=x*a;Que equivaldría literalmente a escribir lo siguiente:
x=x*a;
x=x*a;
x=x*a;
x=x*a;
x=x*a;
x=x*a;
x=x*a;
x=x*a;La expresión en forma de bucle es más genérica y permitiría calcular cualquier potencia entera no negativa de a con sólo cambiar el valor de la variable expon.
La forma general con la que se debe trabajar en un bucle es, salvo excepciones, la que se muestra en la siguiente Figura 4.2. En tal figura se observa como nuestro código al encontrarse con un bucle, debe primero preguntar si se han acabado los elementos que procesar, es decir, si ya no quedan iteraciones que realizar. Es evidente por tanto, que el bucle debe llevar inmerso una estructura condicional. Si la respuesta es afirmativa, el bucle no realizaría ninguna iteración, pero si es negativa, se pasará a realizar la primera iteración. En ella se ejecutará una primera vuelta, donde se leerá y procesará un primer elemento, y finalmente se actualizará el número de iteraciones que quedan o de elementos que faltan por procesar. El proceso se repite cíclicamente, pasando a la segunda pregunta, y así hasta que los elementos se agoten, es decir, no haya más iteraciones que ejecutar.
Como puede observarse, la pregunta "¿Quedan elementos iterar?" o "¿Quedan elementos que procesar?" juega un papel fundamental en el bucle, y hay que saber elegir la variable o condición adecuada para la misma. Un error en la formulación de tal pregunta conduciría a realizar el bucle un número de iteraciones no deseado, y por tanto, el mismo no funcionaría correctamente. Algunas de estas condiciones pueden ser simples, por ejemplo cuando el bucle debe realizar un número fijo de iteraciones, llamémosle N. Entonces, declarando inicialmente una variable con el valor 0, preguntado en la condición si la variable ha alcanzado el valor N, e incrementando en 1 el valor de la misma tras procesar cada iteración, tendríamos construido el bucle de forma correcta. La variable que realiza la cuenta de iteraciones se suele llamar "contador" o "índice" del bucle. Por razones históricas y relacionadas con las matemáticas, las variables que se suelen usar como contadoras en los bucles suelen ser i, j, k, cuando son enteras , dejando las últimas letras del alfabeto para las reales (x, y, z). Aunque, como sabemos, en lenguaje C cualquier variable puede ser declarada de cualquier tipo y jugar el papel que el programador quiera, en este texto seguiremos, en general, tal convenio.
Sin embargo la condición para seguir iterando un bucle, no siempre es tan simple como la de los ejemplos anteriores, sino que el número de iteraciones que debe realizar un bucle puede no ser constante ni estar contenido en una variable, o incluso ser desconocido cuando se escribe el programa. El caso más claro de esta situación se da cuando la condición depende de los valores que introduce el usuario que está ejecutando nuestro programa. Un simple ejemplo de tal situación se muestra cuando se ejecuta un fragmento de código que calcula la raíz cuadrada mientras el número que introduce por teclado el usuario sea positivo o nulo. Cuando el número introducido es negativo, se sale del bucle, y el programa continúa (¡de no ser así, al intentar extraer la raíz cuadrada resultaría en un número complejo, y el lenguaje C no sabe como manejar números complejos!).
Por ello, en el lenguaje C se dispone de estructuras iterativas bastante genéricas, de forma que los bucles con condiciones simples se reducen a casos particulares de los mismos. En las próximas secciones se irán detallando algunos ejemplos de bucles, eligiendo la condición más adecuada, y usando las estructuras iterativas de que dispone el lenguaje C: los bucles while, do-while y for.
-
-