3. Estructuras repetitivas

3.1. ¿Qué es un bucle?

Vamos a ver cómo podemos crear bucles o lazos, es decir, partes del programa que se repitan un cierto número de veces.

Según cómo queramos que se controle ese bucle, tenemos tres posibilidades, que vamos a comentar en primer lugar:

La diferencia entre estos dos últimos es que "while" comprueba la condición antes de repetir las demás sentencias, por lo que puede que estas sentencias ni siquiera se lleguen a ejecutar, en caso de que la condición de entrada fuera falsa. Por el contrario, en un "repeat", la condición se comprueba al final, de modo que las sentencias intermedias se ejecutarán al menos una vez.

Vamos a verlos con más detalle...

3.2. Desde... hasta... (for)

3.2.1 Avanzar de uno en uno

El formato de la orden "for" es

for variable := ValorInicial to ValorFinal do
    Sentencia;

Se podría traducir "desde que la variable valga ValorInicial hasta que valga ValorFinal" (y en cada pasada, su valor aumentará en una unidad).

Como primer ejemplo, vamos a ver un pequeño programa que escriba los números del uno al cinco:

(* FOR1.PAS, Primer ejemplo de "for": contador *)
(* Parte de CUPAS5, por Nacho Cabanes          *)

Program For1;

var
    contador: integer;

begin
    for contador := 1 to 5 do
        writeLn( contador );
end. 
 
(* Resultado:
1
2
3
4
5
*)

Ejercicio propuesto 3.2.1.1: Crea un programa que muestre los números del 10 al 15, cada uno en una línea.
Ejercicio propuesto 3.2.1.2: Crea un programa que muestre los números del 5 al 20, todos ellos en la misma línea, separados por espacios en blanco.
Ejercicio propuesto 3.2.1.3: Crea un programa que escriba 10 veces "Hola" (en la misma línea).

3.2.2. "For" encadenados

Los bucles "for" se pueden enlazar uno dentro de otro, de modo que podríamos escribir las tablas de multiplicar del 1 al 5, dando 5 pasos, cada uno de los cuales incluye otros 10, así:

(* FOR2.PAS, bucles enlazados: tabla de multiplicar *)
(* Parte de CUPAS5, por Nacho Cabanes               *)

Program For2;

var
    tabla, numero: integer;

begin
    for tabla := 1 to 5 do
        for numero := 1 to 10 do
            writeLn( tabla, ' por ', numero ,' es ', tabla * numero );
end. 

(* Resultado:
1 por 1 es 1
1 por 2 es 2
1 por 3 es 3
...
1 por 10 es 10
2 por 1 es 2
...
5 por 10 es 50
*)

Ejercicio propuesto 3.2.2.1: Crea un programa que escriba tres veces seguidas los números del 1 al 3, ocupando 9 líneas distintas en pantalla.
Ejercicio propuesto 3.2.2.2: Crea un programa que escriba 3 líneas, cada una de las cuales contendrá 4 veces la palabra "Hola" seguida por un espacio.

3.2.3. "For" y sentencias compuestas

Hasta ahora hemos visto sólo casos en los que después de "for" había un única sentencia. Si queremos repetir más de una orden, basta encerrarlas entre "begin" y "end" para convertirlas en una sentencia compuesta, como hemos hecho hasta ahora.

Así, vamos a mejorar el ejemplo anterior haciendo que deje una línea en blanco entre tabla y tabla:

(* FOR3.PAS, bucles con sentencias compuestas *)
(* Parte de CUPAS5, por Nacho Cabanes         *)

Program For3;

var
    tabla, numero: integer;

begin
    for tabla := 1 to 5 do
    begin
        for numero := 1 to 10 do
            writeLn( tabla, ' por ', numero ,' es ', tabla * numero );
        writeLn;        (* Línea en blanco *)
    end;
end. 

(* Resultado:
1 por 1 es 1
1 por 2 es 2
1 por 3 es 3
...
1 por 10 es 10

2 por 1 es 2
...
5 por 10 es 50
*)

Como vimos, es muy conveniente usar la escritura indentada, que en este caso ayuda a ver mejor dónde empieza y termina lo que hace cada "for".

Ejercicio propuesto 3.2.3.1: Crea un programa que escriba 10 veces "Hola" (en una línea) y luego la palabra "Adios" (en otra línea).

Esta misma estructura se puede usar para resolver muchos problemas de computación. La emplearemos dentro de poco para recorrer "matrices", y la podemos utilizar también para dibujar algunas figuras geométricas en pantalla, como por ejemplo rectángulos:

(* FOR3BRECT.PAS, bucles con sentencias compuestas *)
(* 2: Dibuja un rectangulo formado por asteriscos  *)
(* Parte de CUPAS5, por Nacho Cabanes              *)
 
Program For3bRect;
 
var
    fila, columna: integer;
    maxFilas, maxColumnas: integer;
 
begin
    maxFilas := 3;
    maxColumnas := 7;
    
    for fila := 1 to maxFilas do
    begin
        for columna := 1 to maxColumnas do
            write('*');
        writeLn;        (* Avance de linea *)
    end;
end. 
 
(* Resultado:
*******
*******
*******
*)

Ejercicio propuesto 3.2.3.2: Crea un programa que escriba un rectángulo de asteriscos, con la anchura y altura que escoja el usuario.
Ejercicio propuesto 3.2.3.3: Crea un programa que escriba un cuadrado de asteriscos, con la anchura que escoja el usuario.
Ejercicio propuesto 3.2.3.4: Crea un programa que escriba un triángulo de asteriscos, que en la primera línea tendrá la anchura que escoja el usuario (por ejemplo, 4), en la siguiente línea tendrá uno menos (3), y así sucesivamente hasta que la última línea tenga anchura 1.
Ejercicio propuesto 3.2.3.5: Crea un programa que escriba un rectángulo hueco, con la anchura y altura que escoja el usuario, que tendrá asteriscos en el borde (primera y última fila, primera y última columna) y espacios en blanco en el interior.

3.2.4. Contar con letras

Una observación: para "contar" no necesariamente hay que usar números, también podemos contar con letras:

(* FOR4.PAS, letras como índices en un bucle *)
(* Parte de CUPAS5, por Nacho Cabanes        *)

Program For4;

var
    letra: char;

begin
    for letra := 'a' to 'z' do
        write( letra );
 end. 

Ejercicio propuesto 3.2.4.1: Crea un programa que escriba las letras de la B a la M (B mayúscula hasta M mayúscula).

3.2.5. Contar de forma decreciente (downto)

Con el bucle "for", tal y como lo hemos visto, sólo se puede contar en forma creciente y de uno en uno. Para contar de forma decreciente, se usa "downto" en vez de "to".

(* FOR5.PAS, "for" que desciende      *)
(* Parte de CUPAS5, por Nacho Cabanes *)

Program For5;

var
    i: integer;

begin
    for i := 5 downto 1 do
        write( i );
end. 
 
(* Resultado:
54321
*)

Ejercicio propuesto 3.2.5.1: Crea un programa que escriba los números del 10 al 1 (de forma descendente).
Ejercicio propuesto 3.2.5.2: Crea un programa que escriba las letras de la Z a la A (de forma descendente).

3.2.6. Contar de 2 en 2 o usando otros incrementos

Para contar de dos en dos (por ejemplo), hay que buscarse la vida: multiplicar por dos o sumar uno dentro del cuerpo del bucle, etc... Eso sí, sin modificar la variable que controla el bucle (deberemos usar cosas como "write(x*2)" en vez de "x := x*2", que pueden dar problemas en algunos compiladores).

(* FOR6.PAS, "for" y numeros no correlativos  *)
(* Parte de CUPAS5, por Nacho Cabanes         *)

program For6;

var
    contador: integer;

begin
    for contador := 1 to 5 do
        writeLn( 10 * contador );
end. 
 
(* Resultado:
10
20
30
40
50
*)

Ejercicio propuesto 3.2.6.1: Un programa que escriba la secuencia de números 2, 4, 6, 8 ... 16.
Ejercicio propuesto 3.2.6.2: Un programa que escriba la secuencia de números 6, 5, 4,..., 1.
Ejercicio propuesto 3.2.6.3: Un programa que escriba la secuencia de números 3, 5, 7,..., 21.
Ejercicio propuesto 3.2.6.4: Un programa que escriba la secuencia de números 12, 10, 8,..., 0..