Estructuras de control; repetitivas
Vamos a ver un poco de teoría primero, porque si no, no
sé dónde meterla ni cómo contarla sin que parezca
un insulto. Perdonadme que esto sea un pelín rollero las
primeras 40 líneas (o así), y ya el resto lo explico con
ejemplos. No la contaría si no fuera necesaria O:)
Estructuras repetitivas
Este tipo de estructuras marcan como orden de ejecución la
reiteración de una serie de acciones basándose en un
bucle.
Un BUCLE (loop, en inglés) es un trozo de algoritmo cuyas
instrucciones son repetidas un cierto número de veces, mientras
se cumple una cierta condición que ha de ser claramente
especificada. La condición podrá ser verdadera o falsa, y
se comprobará en cada paso o iteración
del bucle.
Básicamente, existen tres tipos de estructuras repetitivas; los
bucles "mientras..." (o "while"), los bucles
"repetir... mientras que" (o "do... while") y los bucles
"desde" (o "bucles for"). Vamos a verlas todas dentro de un
ejemplo para clarificar los dos párrafos iniciales, que quedan
tan bonitos como oscuros para quien nunca ha visto un bucle (ni es
verde ni tiene antenas, lo siento %-D ).
Sólo un poco de teoría más antes de pasar a los
ejemplos para cada una de las estructuras repetitivas; hay que hacer
notar que todo bucle consta de tres partes básicas, a saber:
- Decisión: donde se evalúa la
condición y, en caso de ser cierta, se ejecuta el...
- Cuerpo del bucle: son las instrucciones que queremos
ejecutar repetidamente un cierto número de veces.
- Salida del bucle: es la condición que dice
cuándo saldremos de hacer repeticiones ("mientras
protestes, seguirás fregando platos", en cuanto dejas
de protestar, se acabó fregar más platos).
Una forma de controlar un bucle es mediante una variable llamada
CONTADOR, cuyo valor se incrementa o decrementa en una cantidad
constante en cada repetición que se produzca.
También, en los bucles suele haber otro tipo de variables llamadas
ACUMULADOR, cuya misión es almacenar una cantidad variable
resultante de operaciones sucesivas y repetidas. Es como un
contador, con la diferencia que el incremento/decremento es
variable.
Sí, sí, sí... ya sé que no me habeis
entendido ni jota... ya vamos a por ejemplos, y ahí vereis
a qué me refiero.
Una situación real como aplicación
Vamos a suponer que estamos pensando en un programa que deba
REPETIR algunas veces una acción. Por ejemplo, el ordenador
se ha portado mal (el güindoze se ha vuelto a colgar, no sé,
por decir algo };-D ), y, como castigo, le vamos a hacer
imprimir por pantalla 300 MILLONES de veces la frase
"Prometo ser bueno O:-)"
¿Cómo lo hacemos? ¿Escribimos 300.000.000 de veces
la instrucción pertinente? ¡Vaya martirio! ¡Ni con
"Copy'n'Paste"! ¡Se supone que el castigo es para la
máquina, no para uno mismo!
Pues bien, las ESTRUCTURAS REPETITIVAS vienen a rescatarnos de
la tediosa tarea de repetir cientos de líneas que en unas pocas
quedan apañadas.
Estructura MIENTRAS(condición)
En este tipo de estructura, el cuerpo del bucle (ya sabeis, las
acciones que deben ejecutarse repetidas veces) se repite MIENTRAS
se cumple una determinada condición, que especificamos entre
paréntesis.
Su estructura, genéricamente, es esta:
mientras(condición) hacer
acción 1
........
acción N
fin mientras
Aplicado a nuestra situación real, sería:
Declaración de variables
ENTEROS: Contador
fin Declaración variables
inicio
Contador <- 1
mientras(Contador<=300.000.000) hacer
mostrar por pantalla 'Prometo ser bueno O:-)'
Contador <- Contador+1
fin mientras
fin
Con toda la idea, a la variable que "lleva" (por decirlo de alguna
manera) la cuenta de las veces que el bucle se ha ejecutado, la
he llamado contador.
Si os fijais, ANTES de entrar en el bucle le asigno el valor 1.
Recordad que no es recomendable usar variables no inicializadas,
no sabemos qué tienen dentro. En cuanto vemos la palabra
"mientras", ya sabemos que hemos entrado en el bucle. Estamos en
la decisión. ¿Es Contador<=300.000.000?
Yo creo que sí, al menos si es cierto que
1<=300.000.000
Vale, es cierto, así que deben ejecutarse todas las instrucciones
del cuerpo del bucle. En nuestro caso, se mostrará por pantalla
la frase 'Prometo ser bueno O:-)', y, ATENCION, sumo 1
a la variable Contador.
Como Contador valía 1, si ahora le sumo 1, creo que todos estamos
de acuerdo en que ahora Contador vale 2. Llegamos al fin del
mientras. Eso significa que se han terminado las instrucciones
del cuerpo del bucle: debemos volver a evaluar la condición que
tenía el "mientras" entre paréntesis para ver qué
hacemos ahora.
Tenemos que ver si Contador<=300.000.000. Ahora, Contador
valía 2, y se cumple que 2<=300.000.000, con lo que
vuelve a mostrarse por pantalla la expresión 'Prometo ser
bueno O:-)' y de nuevo se suma 1 a Contador, con lo que ahora,
Contador pasa a valer 3. De nuevo llegamos al fin del mientras.
[... un buen rato después...]
Ahora Contador vale 300.000.000. Tenemos que ver si
Contador<=300.000.000. Como es cierto que
300.000.000<=300.000.000, se muestra por pantalla el mensaje
'Prometo ser bueno O:-)', y sumamos 1 a Contador. Ahora,
Contador vale 300.000.001. Llegamos (una vez más) al fin del
mientras, por lo que tenemos que volver a evaluar la condición
entre paréntesis que acompaña al "mientras".
Hay que ver si Contador<=300.000.000. Pero no es cierto que
300.000.001<=300.000.000, es más bien al
revés, o sea, la condición entre paréntesis ES
FALSA, ES UNA MENTIRA. ¿Eso qué quiere decir? Pues
quiere decir que se acabó, que ya no se ejecuta el cuerpo del
bucle, sino que hemos llegado al final, a la salida del bucle, con lo
cual, el bucle ha terminado.
¿Me podría decir alguien qué hubiera pasado si no
incluimos la línea "Contador <- Contador+1" dentro
del cuerpo del bucle?
Sólo adelanto que se llegaría a una situación que
debe evitarse por completo; si tenemos suerte, habrá un error de
desbordamiento, en el peor de los casos, el bucle... completad la
frase ;)
Una nota importante:
Fijaos en una cosa, si, por ejemplo, al empezar nuestro
algoritmo, en vez de hacer
Contador <- 1
hubiéramos hecho
Contador <- 1.000.000.000.000.000.000
aparte de que tenemos posibilidades de salirnos de rango (vamos a
suponer que no fuera así), al entrar en el mientras, la
condición es "Contador<=300.000.000", y Contador
es, claramente, mayor estrictamente que 300.000.000
¿Qué pasaría aquí? Pues es sencillo: la
condición es falsa, luego el cuerpo del bucle se ejecuta CERO
veces. ¿Y qué utilidad tiene poner un bucle para que
luego no se ejecute? En este caso concreto, ninguna, pero puede haber
situaciones en las que sí nos sea útil. De hecho, sucede
a menudo :)
Estructura REPETIR... MIENTRAS QUE(condición)
Aquí, lo que se desea es que un bucle se ejecute AL MENOS
UNA VEZ antes de comprobar la condición de
repetición.
La estructura del REPETIR ... MIENTRAS, genéricamente,
es esta:
repetir
acción 1
........
acción N
mientras que(condición)
Notar que no hace falta poner "fin del repetir", puesto que
está claro que se acaba donde pone "mientras
que(condición)".
Vamos a ver cómo resolver nuestra situación REAL con este
tipo de estructura:
Declaración de variables
ENTEROS: Contador
fin Declaración variables
inicio
Contador <- 1
Repetir
mostrar por pantalla 'Prometo ser bueno O:-)'
Contador <- Contador+1
mientras que(Contador<=300.000.000)
fin
Le seguimos la pista igual que se la hemos seguido al anterior
caso: empezamos con la asignación del valor 1 a la variable
Contador. Llegamos a repetir, quien NO NOS EXIGE, para su
entrada, verificar condición alguna. Mostramos por pantalla
la frase 'Prometo ser bueno O:-)', y sumamos 1 a la variable
Contador, con lo que ahora pasa a valer 2. Y AHORA es cuando
llegamos a la condición: ¿es
Contador<=300.000.000? Yo diría que sí,
así que ale, de vuelta a repetir: mostramos por
pantalla nuestra frase, sumamos 1 a contador...
[... varios minutos después...]
Y ya, por fin, Contador alcanza el valor 300.000.001, con lo
que la comparación dice "¡MENTIRA!", y se acabó
repetir esa tarea.
Nuevamente, os invito a que me digais qué hubiera pasado si no
hiciéramos Contador <- Contador+1 ...
Otra nota importante:
Vamos a remarcar una diferencia IMPORTANTE entre esta estructura
y la anterior "mientras". Si al empezar nuestro algoritmo, en
vez de hacer
Contador <- 1
hubiésemos hecho
Contador <- 1.000.000.000.000.000.000
de nuevo tenemos posibilidades de salirnos de rango, pero va, vamos
a poner que esto que no fuera así; la palabra REPETIR no nos
está pidiendo nada, así que ejecutamos una vez el cuerpo
del bucle. Se nos muestra la frase, se incrementa el contador,
y llegamos al mientras. Como la condición es falsa, no
volvemos a ejecutar el bucle.
¿Cuál es la diferencia?
Pues que con la estructura "mientras", el bucle se ejecutaba
CERO veces, sin embargo, con la estructura
"repetir...mientras que" el bucle se ejecuta UNA vez.
Esta sutil diferencia es la que hace que unas veces se elija al
primero y otras al segundo.
Estructura DESDE
Esta estructura hubiera sido la más adecuada para resolver
nuestra situación real, como veremos a continuación.
La estructura "desde", tiene una pequeña peculiaridad,
y es que ella solita incrementa (o decrementa) DE UNO EN UNO
a la variable que utilicemos como contador.
Su estructura es:
desde Contador<-Inicio hasta Contador=Fin [, decrementar,] hacer
accion 1
........
accion N
fin desde
La palabra "decrementar" entre corchetes significa que
es opcional, es decir, que se puede poner o no. Si NO se pone, por
defecto se asume que al terminar las acciones del bucle, se hará
Contador <- Contador+1
Si ponemos "decrementar", al terminar las acciones del bucle
se hará Contador <- Contador-1. Cuidado con
esto, pues si no especificamos "decrementar", es ILEGAL
escribir
desde Contador<-500 hasta Contador=200 hacer
así como, si escribimos "decrementar", es igualmente
ILEGAL poner
desde Contador<-1 hasta Contador=1257 , decrementar, hacer
No se admite otro tipo de incrementos/decrementos (de 2 en 2,
de 0.5 en 0.5 o de -10 en -10), para ello ya tenemos las
estructuras "mientras" y "repetir...mientras", en las que
nosotros elegíamos el incremento/decremento.
En el lenguaje de programación Pascal esto también
sucede así en los bucles FOR. En otros lenguajes, como
FORTRAN, BASIC o C, el FOR es mucho más potente, siendo el
caso del lenguaje C el que riza el rizo, pudiendo llegar a hacer
verdaderas obras de arte con un "simple" bucle FOR.
Bueno, al grano O:)
Supongo que la estructura está suficientemente clara, sin
embargo, para terminar de clarificarla, vamos a aplicarla a
nuestra BIEN conocida situación REAL:
Declaración de variables
ENTEROS: Contador
fin Declaración variables
inicio
desde Contador<-1 hasta Contador=300.000.000 hacer
mostrar por pantalla 'Prometo ser bueno O:-)'
fin desde
fin
Umm... lo primero que se observa es que no hace
falta asignar el valor 1 a Contador fuera del bucle, puesto que
en la parte
Contador<-Inicio (Contador<-1, en este caso)
ya se asigna automáticamente ese valor.
Lo siguiente es, como ya he mencionado, que no hace falta que
incrementemos el valor de Contador, puesto que es una acción
que se realiza sola en un bucle de este tipo.
Por último, como no hemos usado la palabra decremento, se
asume entonces que incrementamos de 1 en 1 el valor de la
variable Contador.
¿Y cómo se hubiera hecho esto usando decrementos? Pues, por
ejemplo, así:
Declaración de variables
ENTEROS: Contador
fin Declaración variables
inicio
desde Contador<-300.000.000 hasta Contador=1 , decrementar, hacer
mostrar por pantalla 'Prometo ser bueno O:-)'
fin desde
fin
Aunque yo lo he hecho aquí por estar manejando variables de
tipo entero, debo decir que es PELIGROSO usar una comparación
de IGUALDAD entre dos números cualesquiera, y que, para
evitar que por truncamientos, redondeos o algún otro motivo
que lleve a pérdidas de decimales en los números,
en vez de poner = es mejor poner <= o >=. Así,
en nuestros dos ejemplos de bucle mientras, la línea
"decisoria" en el bucle (por llamarla de alguna manera) es más
recomendable escribirla así:
desde Contador<-1 mientras Contador<=300.000.000 hacer
o así, en el caso del decremento:
desde Contador<-300.000.000 mientras Contador>=1 , decrementar,
hacer
Y con esto queda terminado el capítulo de las estructuras
básicas para escribir nuestros algoritmos.
Notar que aquí he cambiado la palabra hasta por la
palabra mientras. Lo que quiere decir es que, mientras que
Contador sea menor o igual que 300.000.000, seguiremos dentro
del bucle.
"Ah, no, con la paliza que nos has dado, ahora la que no se escapa
eres tú. A ver, ¿y qué hay de esas variables llamadas
'acumuladores' que has mencionado antes y que no has usado
para nada?"
Bueno, este... ¡me alegro de que me haga esa pregunta!
:-D
Voy a poner un ejemplo de un acumulador para que se vea claro
para qué sirven. Suponed, por ejemplo, que quiero sumar los
primeros 10 números naturales. Una posible solución
sería esta:
Declaración de variables
ENTEROS: i,acum
fin declaración variables
inicio
acum<-0
desde i<-1 hasta i<=10 hacer
acum<-acum+i
fin desde
mostrar por pantalla acum
fin
Probad a seguirle la pista al bucle, y decidme qué sale.
¿Lo sabríais escribir usando las otras dos estructuras
repetitivas vistas hoy? Si es así, hacedlo }:-)
En el primer paso del bucle, acum vale 0, y le sumamos lo que
vale i, que es 1. Vale, ahora acum vale 1. Se incrementa i
y volvemos al cuerpo del bucle. Sumamos a acum lo que vale i.
Ahora acum vale 1, e i vale 2; tras la suma, acum vale 3.
A eso es a lo que me refería antes, unas cuantas (pero
cuántas) líneas atrás, cuando decía eso
de "ACUMULADOR, cuya misión es almacenar una cantidad
variable resultante de operaciones sucesivas y repetidas. Es como
un contador, con la diferencia que el incremento/decremento es
variable".
A diferencia de nuestro ejemplo REAL, en el que el incremento
era siempre 1, aquí el incremento primero es 1, luego 2,
luego 3... espero que con este ejemplo quede más claro, y si no,
me pedís que ponga otro, y otro... hasta que lo entendais.
Otra nota (no sé cuántas llevo): podemos
anidar los bucles. Esto es: dentro de un bucle, podemos
ejecutar otro bucle, con la condición de que ese otro bucle
esté COMPLETAMENTE CONTENIDO dentro del primero, si no,
el algoritmo no es válido.
Por ejemplo: El caso más típico de bucle anidado es querer
asignar valores a una matriz. Quien no sepa qué es una matriz,
que espere al capítulo 7
del curso. Supongamos que nuestra matriz
tiene por dimensiones 10 y 12, y queremos asignar valores
a cada una de sus componentes. Esto lo haremos así:
Declaración de variables
A: Matriz(1..10,1..12) de ENTEROS
ENTEROS: i,j {variables para recorrer la matriz}
Fin declaración de variables
Inicio
desde i<-1 hasta i<=10 hacer
desde j<-1 hasta j<=12 hacer
A(i,j)<-i+j
fin desde {j}
fin desde {i}
Fin
Como veis, el bucle que gobierna la variable j está totalmente
contenido dentro del bucle gobernado por la variable i, y, por
cada iteración del bucle que dirige i, se ejecutan las 12
repeticiones del bucle dirigido por j. ¿Quién me dice
cómo quedará nuestra matriz tras la asignación
de valores que hemos hecho? Para no marearos, tomar las filas con la
i, y las columnas con la j, como es usual en la notación de
matrices.
Vamos ahora a por lo que a mí me gusta... los ejercicios
};-D
- Suponed que teneis un vector de 20 componentes (ver el
capítulo 7
del curso). Escribid un algoritmo que lo invierta, es decir,
la componente 1 pasa a ser la 20, la 2 pasa a ser la 19, la 3
será la 18... y así con todas.
- Escribid un algoritmo que muestre por pantalla las tablas de
multiplicar del 1 al 10. PISTA: Usad bucles anidados.
- Escribid un algoritmo que muestre por pantalla la suma de los
cuadrados de los N primeros números.
- Escribid un algoritmo que calcule la media aritmética de
una serie de N números.
Eso es todo (ahora sí), nos vemos en la próxima entrega,
en la que hablaremos de... FUNCIONES Y PROCEDIMIENTOS.