lunes, 22 de noviembre de 2010

Estructuras Iterativas

Materia: Lenguajes de Programacion.

CONTROL DE FLUJO: ESTRUCTURAS ITERATIVAS

La iteración permite controlar mediante expresiones lógicas la repetición de una o varias instrucciones dentro del programa. Los bloques con las instrucciones que se repiten se conocen como ciclos o loops. La expresión que controla la ejecución del ciclo se conoce como condición de control.

El lenguaje de programación C cuenta con tres instrucciones para especificar iteraciones. Aunque las tres instrucciones son lógicamente equivalentes, sus diferencias se radican principalmente en la forma en que se controla la continuación o terminación del ciclo (especificación y verificación). Las instrucciones son:

• while
• do-while
• for
En realidad, casi todos los lenguajes cuentan con instrucciones parecidas (si no iguales) o que funcionan de un modo muy similar a las que vamos a ver aquí. Las estructuras iterativas de control de flujo se ocupan de repetir una serie de líneas de código tantas veces como el programador indique o bien hasta que se de una cierta condición. A estas estructuras también se les llama bucles.

BUCLES FOR
Los bucles for van asignando valores a una variable desde un valor inicial hasta un valor final, y cuando la variable contiene un valor que está fuera del intervalo el bucle termina. Veamos la sintaxis para hacernos mejor a la idea:

for (var=inicial;condición;siguientevalor)
{
Instrucciones
}
Sé que esto es algo difícil de leer, incluso para aquellos que hayan programado en otros lenguajes, puesto que los bucles for de C no se parecen mucho, en cuanto a su sintaxis, al resto de los bucles for de los otros lenguajes, así que trataré de explicarlo con detenimiento. Como veis, tras la sentencia for se indican las especificaciones del bucle entre paréntesis. Dichas especificaciones están divididas en tres partes separadas por punto y coma: la parte de asignación del valor inicial en primer lugar; la parte que verifica la continuidad del bucle (mediante una condición) en segundo lugar; y la parte en que se calcula el siguiente valor en tercer lugar. Pongamos un ejemplo: vamos a calcular el factorial de un número dado, que se encuentra almacenado en la variable num. Se podría hacer de dos formas:
for (byte i=num; i>1 ; i--)
{
fact*=i;
}
O bien:
for (byte i=1; i<=num ; i++)
{
fact*=i;
}
En primer lugar se asigna a la variable i el valor de num (vamos a suponer que num vale 3), es decir, después del primer paso, el valor de i es 3. Posteriormente se comprueba si dicha variable es mayor que 1, es decir, si 3>1. Como la condición del segundo paso se cumple se ejecuta el código del bucle en el tercer paso, fact*=i, con lo que fact (que valía 1) ahora vale 3 (1*3). En el cuarto paso se asigna el siguiente valor a i (i--), con lo que, ahora, i valdrá 2. En el quinto se vuelve a comprobar si i es mayor que 1, y como esto se cumple, el sexto paso vuelve a ejecutar el código del bucle (de nuevo, fact*=i), con lo que ahora fact vale 6 (3*2). El séptimo paso es idéntico al cuarto, es decir, se asigna el siguiente valor a la variable i (de nuevo, i--), con lo que ahora i valdría 1. El octavo paso es idéntico al quinto, comprobando por lo tanto si i es mayor que 1. Sin embargo esta vez, la condición no se cumple (1 no es mayor que 1, sino igual), por lo que la ejecución saldría del bucle y ejecutaría la siguiente línea del programa que esté fuera de él. Date cuenta de que el bucle se seguirá ejecutando siempre que la condición ( i>1 ) se cumpla, y dejará de ejecutarse cuando la condición no se cumpla. Por lo tanto, no habría sido válido poner i==2 en lugar de i>1, ya que esta condición se cumpliría únicamente cuando num valiera 2, pero no en cualquier otro caso. ¿Serías capaz de ver cómo funcionaría el otro bucle? Venga, inténtalo.
BUCLES FOR ANIDADOS
Efectivamente, se pueden colocar bucles for dentro de otros bucles for, con lo que obtendríamos lo que se llaman los bucles for anidados. Son también muy útiles: por ejemplo, piensa que tienes almacenadas unas cuantas facturas en una base de datos, y quieres leerlas todas para presentarlas en pantalla. El problema está en que cada factura tiene una o varias líneas de detalle. ¿Cómo podríamos hacer para cargar cada factura con todas sus líneas de detalle? Pues usando bucles anidados. Colocaríamos un bucle for para cargar las facturas, y otro bucle for dentro de él para que se cargaran las líneas de detalle de cada factura. Así, el segundo bucle se ejecutará completo en cada iteración del primer bucle. Veamos un ejemplo que nos aclare todo esto un poco más:

using System;
namespace BuclesAnidados
{
class BuclesAnidadosApp
{
static void Main()
{
for (int i=1; i<=3; i++)
{
Console.WriteLine("Factura número {0}", i);
Console.WriteLine("Detalles de la factura");
for (int j=1; j<=3; j++)
{
Console.WriteLine(" Línea de detalle {0}", j);
}
Console.WriteLine();
}
string a=Console.ReadLine();
}
}
}
Como ves, el bucle "j" está dentro del bucle "i", de modo que se ejecutará completo tantas veces como se itere el bucle i. Por este motivo, la salida en consola sería la siguiente:
Factura número 1

Detalles de la factura

Línea de detalle 1

Línea de detalle 2

Línea de detalle 3
Factura número 2

Detalles de la factura

Línea de detalle 1

Línea de detalle 2

Línea de detalle 3
Factura número 3

Detalles de la factura

Línea de detalle 1

Línea de detalle 2

Línea de detalle 3
Otra forma de anidar los bucles es utilizando solamente una única sentencia for, aunque no es un modo muy recomendable de hacerlo puesto que resulta mucho más difícil de leer. El siguiente código:

for (int i=1, int j=1; i<=3, j<=3; i++, j++)
{
...
}
Sería el equivalente a esto otro:
for (int i=1; i<=3; i++)
{
for (int j=1; j<=3; j++)
{
...
}
}
BUCLES WHILE
Bien, para los que no sepan inglés, "while" significa "mientras", de modo que ya os podéis hacer la idea: un bucle while se repetirá mientras una condición determinada se cumpla, o sea, devuelva true. Veamos su sintaxis:

while (expresión bool)
{
Instrucciones
}

Efectivamente, las "Instrucciones" que se hallen dentro del bucle while se ejecutarán continuamente mientras la expresión de tipo boolean retorne true. Por ejemplo, podemos escribir un bucle while para pedir una contraseña de usuario. Algo así:

using System;
namespace BuclesWhile
{
class BuclesWhileApp
{
static void Main()
{
string Clave="Compadre, cómprame un coco";
string Res="";
while (Res!=Clave)
{
Console.Write("Dame la clave: ");
Res=Console.ReadLine();
}
Console.WriteLine("La clave es correcta");
string a=Console.ReadLine();
}
}
}

En este pequeño ejemplo el programa pedirá una y otra vez la clave al usuario, y cuando este teclee la clave correcta será cuando finalice la ejecución del mismo. Así, la salida en la consola de este programa sería algo como esto (en rojo está lo que se ha tecleado durante su ejecución):

Dame la clave: No quiero

Dame la clave: Que no

Dame la clave: eres contumaz, ¿eh?

Dame la clave: Vaaaaale

Dame la clave: Compadre, cómprame un coco

La clave es correcta

¿Alguna pregunta? ¿Que qué habría pasado si la condición no se hubiera cumplido antes de ejecutar el bucle, es decir, si Res ya contuviera lo mismo que Clave antes de llegar al while? Bien, pues, en ese caso, el bucle no se hubiera ejecutado ninguna vez, es decir, al comprobar que la expresión de tipo boolean retorna false, la ejecución del programa pasa a la primera línea que se encuentra a continuación del bucle. Vamos a verlo para que te quede más claro. Modificaremos ligeramente el ejemplo anterior, así:

using System;
namespace BuclesWhile
{
class BuclesWhileApp
{
static void Main()
{
string Clave="Compadre, cómprame un coco";
string Res=Clave;
while (Res!=Clave)
{
Console.Write("Dame la clave: ");
Res=Console.ReadLine();
}
Console.WriteLine("La clave es correcta");
string a=Console.ReadLine();
}
}
}

En efecto, en este caso, la salida en consola sería la siguiente:

La clave es correcta
Ya que la ejecución no pasa por el bucle. Bueno, ya veis que es muy sencillo. Por cierto, luego os propondré algunos ejercicios para que practiquéis un poco todo esto de los bucles (a ver si pensabais que os ibais a escaquear).
BUCLES DO
Ciertamente, estos bucles tienen mucho que ver con los bucles while. La diferencia es que estos se ejecutan siempre al menos una vez, mientras que los bucles while, como acabamos de ver antes, pueden no ejecutarse ninguna vez. Veamos la sintaxis de los bucles "do":

do
{
Instrucciones
} while (expresión bool);

Como ves, también hay un while y una expresión boolean, pero en este caso se encuentra al final. De este modo, la ejecución pasará siempre por las instrucciones del bucle una vez antes de evaluar dicha expresión. Vamos a rehacer el ejemplo anterior cambiando el bucle while por un bucle do:

using System;
namespace BuclesDo
{
class BuclesDoApp
{
static void Main()
{
string Clave="Compadre, cómprame un coco";
string Res="";
do
{
Console.Write("Dame la clave: ");
Res=Console.ReadLine();
} while (Res!=Clave);
Console.WriteLine("La clave es correcta");
string a=Console.ReadLine();
}
}
}
El resultado sería el mismo que antes. La diferencia está en que aquí daría exactamente lo mismo lo que valiera la variable Res antes de llegar al bucle, puesto que este se va a ejecutar antes de comprobar dicho valor, y al ejecutarse, el valor de Res se sustituye por lo que se introduzca en la consola. Por lo tanto, repito, los bucles do se ejecutan siempre al menos una vez.
Por otro lado tenemos otro tipo de bucle, los bucles foreach, pero no hablaremos de ellos hasta que hayamos visto arrays e indizadores. Tened un poco de paciencia, que todo se andará.
INSTRUCCIONES DE SALTO
Las instrucciones de salto permiten modificar también el flujo del programa, forzando la siguiente iteración de un bucle antes de tiempo, o la salida del mismo o bien mandando la ejecución directamente a un punto determinado del programa (esto último está altamente perseguido y penado por la ley, o sea, los jefes de proyecto). Son pocas y muy sencillas, así que podéis estar tranquilos, que no os voy a soltar otra biblia con esto...


LA INSTRUCCIÓN BREAK
Algo hemos visto ya sobre la instrucción break. ¿Cómo que no? Anda, repásate la entrega anterior, hombre... Mira que se te ha olvidado pronto... En fin... a lo que vamos. La instrucción break fuerza la salida de un bucle antes de tiempo o bien de una estructura de control de flujo condicional en la que se encuentre (un switch). Ahora nos fijaremos en los bucles, que es donde andamos. Pondremos un ejemplo sencillo: El siguiente programa escribirá múltiplos de 5 hasta llegar a 100:

using System;
namespace InstruccionBreak
{
class InstruccionBreakApp
{
static void Main()
{
int num=0;
while (true)
{
Console.WriteLine(num);
num+=5;
if (num>100) break;
}
string a=Console.ReadLine();
}
}
}

¿Qué es eso de while (true)? Pues un bucle infinito. ¿No decíamos que dentro de los paréntesis había que colocar una expresión boolean? Pues entonces... true es una expresión boolean. De este modo, el bucle es infinito (claro, true siempre es true). Sin embargo, cuando la variable num tiene un valor mayor que 100 la ejecución del bucle terminará, pues se ejecuta una instrucción break.


LA INSTRUCCIÓN CONTINUE
La instrucción continue fuerza la siguiente iteración del bucle donde se encuentre (que puede ser un bucle for, while, do o foreach). Como esto se ve muy bien con un ejemplo, vamos con ello: El siguiente programa mostrará todos los números del uno al veinte a excepción de los múltiplos de tres:

using System;
namespace InstruccionContinue
{
class InstruccionContinueApp
{
static void Main()
{
for (int i=1; i<=20; i++)
{
if (i % 3 == 0) continue;
Console.WriteLine(i);
}
string a=Console.ReadLine();
}
}
}

En este ejemplo, el bucle for va asgnando valores a la variable i entre 1 y 20. Sin embargo, cuando el valor de i es tres o múltiplo de tres (es decir, cuando el resto de la división entre i y 3 es cero) se ejecuta una instrucción continue, de modo que se fuerza una nueva iteración del bucle sin que se haya escrito el valor de i en la consola. Por este motivo, aparecerían todos los números del uno al veinte a excepción de los múltiplos de tres.

Bibliografias:

http://www2.ing.puc.cl/~iic11021/materia/cap5.htm

http://www.exes.es/ManNET/index.asp?Pg=Entrega10.htm

1 comentario: