AnteriorPosterior

Tema 6: Bucles

  Curso: Curso de Pascal, por Nacho Cabanes

Curso de Pascal. Tema 6: Bucles.

Vamos a ver cómo podemos crear bucles, 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:

  • for..to: La orden se repite desde que una variable tiene un valor inicial hasta que alcanza otro valor final (un cierto NÚMERO de veces).
  • while..do: Repite una sentencia MIENTRAS que sea cierta la condición que indicamos (se verá en el apartado 6.5).
  • repeat..until: Repite un grupo de sentencias HASTA que se dé una condición (se verá en el apartado 6.6).


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, si la condición de entrada es falsa. En "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...

El formato de "for" es

for variable := ValorInicial to ValorFinal do
Sentencia;

Se podría traducir por algo como "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 diez:

 {--------------------------}
 {  Ejemplo en Pascal:      }
 {                          }
 {    Primer ejemplo de     }
 {    "for": contador       }
 {    FOR1.PAS              }
 {                          }
 {  Este fuente procede de  }
 {  CUPAS, curso de Pascal  }
 {  por Nacho Cabanes       }
 {                          }
 {  Comprobado con:         }
 {    - Free Pascal 2.2.0w  }
 {    - Turbo Pascal 7.0    }
 {    - Turbo Pascal 5.0    }
 {    - Surpas 1.00         }
 {--------------------------}
 
 Program For1;
 
 var
   contador: integer;
 
 begin
   for contador := 1 to 10 do
     writeln( contador );
 end. 
 

Ejercicio propuesto: Crea un programa que muestre los números del 10 al 15, cada uno en una línea.
Ejercicio propuesto: 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: Crea un programa que escriba 10 veces "Hola" (en la misma línea).

Tema 6.2: "For" encadenados.

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

 {--------------------------}
 {  Ejemplo en Pascal:      }
 {                          }
 {    Segundo ejemplo de    }
 {    "for": bucles enlaza- }
 {    dos -> tabla de       }
 {    multiplicar           }
 {    FOR2.PAS              }
 {                          }
 {  Este fuente procede de  }
 {  CUPAS, curso de Pascal  }
 {  por Nacho Cabanes       }
 {                          }
 {  Comprobado con:         }
 {    - Free Pascal 2.2.0w  }
 {    - Turbo Pascal 7.0    }
 {    - Turbo Pascal 5.0    }
 {    - Surpas 1.00         }
 {--------------------------}
 
 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. 
 

Ejercicio propuesto: Crea un programa que escriba tres veces seguidas los números del 1 al 3.
Ejercicio propuesto: Crea un programa que escriba 3 líneas, cada una de las cuales contendrá 4 veces la palabra "Hola".

 


Tema 6.3: "For" y sentencias compuestas.

Hasta ahora hemos visto sólo casos en los que después de "for" había un única sentencia. ¿Qué ocurre 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:

 {--------------------------}
 {  Ejemplo en Pascal:      }
 {                          }
 {    Tercer ejemplo de     }
 {    "for": bucles con     }
 {    sentencias compuestas }
 {    FOR3.PAS              }
 {                          }
 {  Este fuente procede de  }
 {  CUPAS, curso de Pascal  }
 {  por Nacho Cabanes       }
 {                          }
 {  Comprobado con:         }
 {    - Free Pascal 2.2.0w  }
 {    - Turbo Pascal 7.0    }
 {    - Turbo Pascal 5.0    }
 {    - Surpas 1.00         }
 {--------------------------}
 
 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. 
 

Recordad, como vimos, que es muy conveniente usar laescritura indentada, que en este caso ayuda a ver dónde empieza y termina lo que hace cada "for"... espero O:-)


Tema 6.4: Contar sin números.

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

 {--------------------------}
 {  Ejemplo en Pascal:      }
 {                          }
 {    Cuarto ejemplo de     }
 {    "for": letras como    }
 {    índices en un bucle   }
 {    FOR4.PAS              }
 {                          }
 {  Este fuente procede de  }
 {  CUPAS, curso de Pascal  }
 {  por Nacho Cabanes       }
 {                          }
 {  Comprobado con:         }
 {    - Free Pascal 2.2.0w  }
 {    - Turbo Pascal 7.0    }
 {    - Turbo Pascal 5.0    }
 {    - Surpas 1.00         }
 {--------------------------}
 
 Program For4;
 
 var
   letra: char;
 
 begin
   for letra := 'a' to 'z' do
     write( letra );
 end. 
 

Como último comentario: 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".

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

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 (usar cosas como "write(x*2)" en vez de "x := x*2", que pueden dar problemas en algunos compiladores).

Pero todo eso os dejo que lo investigueis con los ejercicios... }:-)


Tema 6.4 (b): Ejercicios sobre "For".

Ejercicio propuesto: Un programa que escriba la secuencia de números 2, 4, 6, 8 ... 16.
Ejercicio propuesto: Un programa que escriba la secuencia de números 6, 5, 4,..., 1.
Ejercicio propuesto: Un programa que escriba la secuencia de números 3, 5, 7,..., 21.
Ejercicio propuesto: Un programa que escriba la secuencia de números 12, 10, 8,..., 0..
Ejercicio propuesto: Crea un programa que sume dos vectores, cuyos componentes indicará el usuario. Por ejemplo, la suma de (1,2,3) y (7,11,-1) sería (8,13,2).
Ejercicio propuesto: Crea un programa que halle el producto escalar dos vectores, cuyos componentes indicará el usuario.
Ejercicio propuesto: Crea un programa que multiplique dos matrices.
Ejercicio propuesto: Para los más osados (y que conozcan el problema), un programa de resolución de sistemas de ecuaciones por Gauss.

Tema 6.5: "While".

While

Vimos como podíamos crear estructuras repetitivas con la orden "for", y comentamos que se podía hacer también con "while..do", comprobando una condición al principio, o con "repeat..until", comprobando la condición al final de cada repetición. Vamos a ver estas dos con más detalle:

La sintaxis de "while" es

while condición do
sentencia;

Que se podría traducir como "MIENTRAS se cumpla la condición HAZ sentencia".

Un ejemplo que nos diga la longitud de todas las frases que queramos es:

 {--------------------------}
 {  Ejemplo en Pascal:      }
 {                          }
 {    Ejemplo de "While":   }
 {    muestra la longitud   }
 {    del texto tecleado    }
 {    WHILE1.PAS            }
 {                          }
 {  Este fuente procede de  }
 {  CUPAS, curso de Pascal  }
 {  por Nacho Cabanes       }
 {                          }
 {  Comprobado con:         }
 {    - Turbo Pascal 7.0    }
 {    - Turbo Pascal 5.0    }
 {--------------------------}
 
 Program While1;
 
 var
   frase: string;
 
 begin
   writeln('Escribe frases, y deja una línea en blanco para salir');
   write( '¿Primera frase?' );
   readln( frase );
   while frase <> '' do
     begin
     writeln( 'Su longitud es ', length(frase) );
      { SURPAS 1.00 no reconoce "length" }
     write( '¿Siguiente frase?' );
     readln( frase )
     end
 end. 
 

En el ejemplo anterior, sólo se entra al bloque begin-end (una sentencia compuesta) si la primera palabra es correcta (no es una línea en blanco). Entonces escribe su longitud, pide la siguiente frase y vuelve a comprobar que es correcta.

Como comentario casi innecesario, length es una función que nos dice cuantos caracteres componen una cadena de texto.

Si ya de principio la condición es falsa, entonces la sentencia no se ejecuta ninguna vez, como pasa en este ejemplo:

while (2<1) do
writeln('Dos es menor que uno');

Ejercicio propuesto: Crea un programa vaya sumando los números que el usuario introduzca, y mostrando dicha suma, hasta que introduzca el número 0, usando "while".
Ejercicio propuesto: Crea un programa que pida al usuario su contraseña. Deberá terminar cuando introduzca como contraseña la palabra "acceso", pero volvérsela a pedir tantas veces como sea necesario.
Ejercicio propuesto: Crea un programa que escriba en pantalla los números del 1 al 10, usando "while".

Tema 6.6: "Repeat".

Repeat..until

Para "repeat..until", la sintaxis es

repeat
sentencia;
...
sentencia;
sentencia
until condición;

Es decir, REPITE un grupo de sentencias HASTA que la condición sea cierta. Ojo con eso: es un grupo de sentencias, no sólo una, como ocurría en "while", de modo que ahora no necesitaremos "begin" y "end" para crear sentencias compuestas.

El conjunto de sentencias se ejecutará al menos una vez, porque la comprobación se realiza al final.

Como último detalle, de menor importancia, no hace falta terminar con punto y coma la sentencia que va justo antes de "until", al igual que ocurre con "end".

Un ejemplo clásico es la "clave de acceso" de un programa, que iremos mejorando cuando veamos distintas formas de "esconder" lo que se teclea, bien cambiando colores o bien escribiendo otras letras, como * (empleando métodos que veremos en el tema 10, y lo aplicaremos a un "juego del ahorcado").

 {--------------------------}
 {  Ejemplo en Pascal:      }
 {                          }
 {    Ejemplo de "Repeat":  }
 {    comprobación de una   }
 {    clave de acceso       }
 {    REPEAT.PAS            }
 {                          }
 {  Este fuente procede de  }
 {  CUPAS, curso de Pascal  }
 {  por Nacho Cabanes       }
 {                          }
 {  Comprobado con:         }
 {    - Free Pascal 2.2.0w  } 
 {    - Turbo Pascal 7.0    }
 {    - Turbo Pascal 5.0    }
 {    - Surpas 1.00         }
 {--------------------------}
 
 program ClaveDeAcceso;
 
 var
   ClaveCorrecta, Intento: String[30];
 
 begin
   ClaveCorrecta := 'PaskalForever';
   repeat
     WriteLn( 'Introduce la clave de acceso...' );
     ReadLn( Intento )
   until Intento = ClaveCorrecta
   (* Aquí iría el resto del programa *)
 end. 
 

Se entiende, ¿verdad?

Ejercicio propuesto: Crea un programa que pida números positivos al usuario, y vaya calculando la suma de todos ellos (terminará cuando se teclea un número negativo o cero), usando "repeat".
Ejercicio propuesto: Crea un programa que escriba en pantalla los números pares del 26 al 10 (descen­diendo), usando "repeat".
Ejercicio propuesto: Crea un programa que pida al usuario su nombre de usuario y su contraseña, y no le permita seguir hasta que introduzca como nombre "yo" y como contraseña "acceso", usando "repeat"

Es bastante por hoy. Ahora os toca experimentar a vosotros.


Curso de Pascal. Tema 6.7: Ejercicios sobre "While" y "Repeat".

Ejercicio propuesto: Mejorar el programa de la clave de acceso con "while" (6.5b), para que avise de que la clave no es correcta..
Ejercicio propuesto: Mejorar el programa de la clave de acceso con "repeat" (6.6c), para que avise de que la clave no es correcta..
Ejercicio propuesto: Mejorar más todavía el programa de la clave de acceso con "while", para que sólo haya tres intentos.
Ejercicio propuesto: Mejorar más todavía el programa de la clave de acceso con "while", para que sólo haya tres intentos.


Por cierto, si alguien viene de Basic puede que se pregunte "¿Y mi goto? ¿No existe en Pascal?" Pues sí, existe, pero no contaremos nada sobre él por ahora, porque va en contra de todos los principios de la Programación Estructurada, su uso sólo es razonable en casos muy concretos que todavía no necesitamos.

Ya se verá más adelante la forma de usarlo.


Curso de Pascal. Tema 6.8: Ejemplo - Adivinar números.

Vamos a ver un ejemplo sencillo que use parte de lo que hemos visto hasta ahora.

Será un programa de adivinar números: un primer usuario deberá introducir un número que un segundo usuario deberá adivinar. También deberá indicar cuantos intentos va a permitir que tenga. Entonces se borra la pantalla, y se comienza a preguntar al segundo usuario qué número cree que es. Se le avisará si se pasa o se queda corto. El juego termina cuando el segundo usuario agote todos sus intentos o acierte el número propuesto.

Allá va...

 {--------------------------}
 {  Ejemplo en Pascal:      }
 {                          }
 {    Ejemplo de "Repeat":  }
 {    adivinar un número    }
 {    ADIVINA.PAS           }
 {                          }
 {  Este fuente procede de  }
 {  CUPAS, curso de Pascal  }
 {  por Nacho Cabanes       }
 {                          }
 {  Comprobado con:         }
 {    - Turbo Pascal 7.0    }
 {    - Turbo Pascal 5.0    }
 {    - Surpas 1.00         }
 {    - Tmt Pascal Lt 1.20  }
 {--------------------------}
 
 program adivina;
 
 var
   correcto,             { Número que se debe acertar }
   probado,              { El número que se prueba }
   maxIntentos,          { Máximo de intentos permitido }
   intentoAct: integer;  { El número de intento actual }
   i: byte;              { Para bucles }
 
 begin
   { Primero pedimos los datos iniciales }
   write('Usuario 1: Introduzca el número a adivinar. ');
 
   readln(correcto);
   write('¿Cuantos intentos va a permitir? ');
   readln(maxIntentos);
   { Borramos la pantalla de forma "fea" pero que sirve }
   for i:= 1 to 25 do writeln;
 
   intentoAct := 0;  { Aún no ha probado }
   { Comienza la parte repetitiva }
   repeat
 
     writeln;
     write('Usuario 2: Introduzca un número. ');
     readln(probado);                   { Pedimos un número }
 
     if probado > correcto then         { Puede ser mayor }
 
       writeln('Se ha pasado!')
       else if probado < correcto then  { Menor }
           writeln('Se ha quedado corto!')
           else writeln('Acertó!');     { O el correcto }
 
     intentoAct := intentoAct + 1;      { Ha gastado un intento más }
     writeln('Ha gastado ', intentoAct,
       ' intentos de ',                 { Le recordamos cómo va }
       maxIntentos,' totales.');
 
   until (intentoAct >= maxIntentos)    { Seguimos hasta que gaste todos }
     or (probado = correcto);           { o acierte }
 
   if (intentoAct >= maxIntentos) and (probado <> correcto) then
     writeln('Lo siento, ha agotado sus intentos.');
 
 end. 
 

(Más adelante podrás encontrar un ejemplo más desarrollado: el juego del ahorcado, como parte del tema 10).

Curso de Pascal. Tema 6.9: Arrays y estructuras repetitivas.

Los arrays se pueden crear y recorrer con mucha facilidad usando estructuras repetitivas, como "for". Por ejemplo, podríamos pedir al usuario 5 números y mostrarlos al revés de la siguiente forma:

 {--------------------------}
 {  Ejemplo en Pascal:      }
 {                          }
 {    Ejemplo de uso de     }
 {    arrays (01):          }
 {    Pedir datos y mostrar }
 {    al revés              }
 {    ARRAY01.PAS           }
 {                          }
 {  Este fuente procede de  }
 {  CUPAS, curso de Pascal  }
 {  por Nacho Cabanes       }
 {                          }
 {  Comprobado con:         }
 {    - Free Pascal 2.4.2w  }
 {--------------------------}
 
 Program Array01;
 
 var
   datos: array[1..5] of integer;
   i: integer;
 
 begin
   for i := 1 to 5 do
   begin
     write('Deme el dato ',i,': ');
     readln(datos[i]);
   end;
 
   write('Los datos al reves son: ');
   for i := 5 downto 1 do
     write(datos[i], ' ');
   writeLn;
 end. 
 

Ejercicio propuesto: Crea un programa que pida al usuario cuatro nombres y luego muestre todos ellos en la misma línea.

 

Es muy habitual que no sepamos cuántos datos querremos guardar. En ese caso, podemos crear un array "sobredimensionado" (más grande de lo que esperamos necesitar), y llevar un contador en qué posición tenemos que guardar el siguiente dato, y hasta qué posición debemos recorrer. La única precaución adicional es asegurarnos de que no "nos salimos" del array:

 {--------------------------}
 {  Ejemplo en Pascal:      }
 {                          }
 {    Ejemplo de uso de     }
 {    arrays (02):          }
 {    Pedir datos y mostrar }
 {    al revés, en un array }
 {    parcialmente lleno,   }
 {    usando un contador    }
 {    ARRAY02.PAS           }
 {                          }
 {  Este fuente procede de  }
 {  CUPAS, curso de Pascal  }
 {  por Nacho Cabanes       }
 {                          }
 {  Comprobado con:         }
 {    - Free Pascal 2.4.2w  }
 {--------------------------}
 
 Program Array02;
 
 const
   capacidad = 200;
 
 var
   datos: array[1..capacidad] of integer;
   cantidad: integer; { Los datos existentes }
   i: integer;  { Para bucles }
 
 
 begin
   cantidad := 0;
   repeat
     if cantidad >= capacidad then
       writeLn('No caben mas datos!')
     else
       begin
       cantidad := cantidad+1;
       write('Deme el dato ',cantidad,' (999 para salir): ');
       readln(datos[cantidad]);
       end;
   until datos[cantidad]=999;
 
   { El ultimo dato no hay que guardarlo }
   cantidad := cantidad-1;
 
   { Al final, muestro todos }
   write('Cantidad de datos: ', cantidad);
   write('Los datos al reves son: ');
   for i := cantidad downto 1 do
     write(datos[i], ' ');
   writeLn;
 end. 
 

Ejercicio propuesto: Crea un programa que pida al usuario varios nombres, hasta que introduzca "fin" y luego diga si alguno de ellos es "Nacho".

 

Una alternativa es tener un "centinela" que indique cual es la primera ficha no válida. Por ejemplo, si las fichas almacenan números del 1 al 10, en la primera ficha vacía podríamos guardar el número -1, o el 999, o cualquier otro no válido, y para hacer las búsquedas y lecturas usarías un "while" en vez de un "for":

 {--------------------------}
 {  Ejemplo en Pascal:      }
 {                          }
 {    Ejemplo de uso de     }
 {    arrays (03):          }
 {    Pedir datos y mostrar }
 {    al revés, en un array }
 {    parcialmente lleno,   }
 {    usando un centinela   }
 {    ARRAY03.PAS           }
 {                          }
 {  Este fuente procede de  }
 {  CUPAS, curso de Pascal  }
 {  por Nacho Cabanes       }
 {                          }
 {  Comprobado con:         }
 {    - Free Pascal 2.4.2w  }
 {--------------------------}
 
 Program Array03;
 
 const
   capacidad = 200;
 
 var
   datos: array[1..capacidad] of integer;
   cantidad: integer; { Los datos existentes }
   i: integer;  { Para bucles }
 
 
 begin
   cantidad := 0;
   repeat
     if cantidad >= capacidad then
       writeLn('No caben mas datos!')
     else
       begin
       cantidad := cantidad+1;
       write('Deme el dato ',cantidad,' (999 para salir): ');
       readln(datos[cantidad]);
       end;
   until datos[cantidad]=999;
 
   { Al final, muestro todos }
   write('Los datos leidos son: ');
   i := 1;
   while datos[i] <> 999 do
     begin
     write(datos[i], ' ');
     inc(i);
     end;
   writeLn;
 end. 
 

Suele ser más eficiente usar un contador. Generalmente, sólo tiene sentido usar centinelas cuando la situación es un poco más complicada. Por ejemplo, si las fichas borradas no se eliminan realmente, sino que se marcan como disponibles. En ese caso, podríamos usar dos centinelas: la marca de final de datos (por ejemplo, 999) y la marca de ficha borrada (por ejemplo, 998), que se comprobaría con un "if" para no escribirla en pantalla cuando se muestran todos lo datos.

Ejercicio propuesto: Crea un programa que pida al usuario varios nombres, luego le pregunte tres que quiera borrar (para lo que usará "centinelas") y luego muestre todos los nombres que no se han borrado.

 

En el tema 7 tienes un ejemplo más detallado, que usa un "array de records" para crear una agenda.

Actualizado el: 29-01-2012 17:26

AnteriorPosterior