ESTRUCTURAS REPETITIVAS

ESTRUCTURA DE REPETICION FOR...NEXT

Introduccion a las estructuras de control repetitivas

Como detener la ejecucion de un programa

Instruccion FOR...NEXT

 

1.9.1 - Introduccion a estructuras de control repetitivas

En los siguientes temas vamos a ver las instrucciones que existen en la Programacion Estructurada para conseguir que un bloque de instrucciones se puedan ejecutar más de una vez sin necesidad de escribirlas repetidas en el listado del codigo fuente del programa.

En lenguaje ensamblador y en las versiones antiguas da BASIC se usan instrucciones de tipo GOTO que continúan la ejecucion del programa en otra parte, pero esto da lugar a programas muy reliados (Codigo "espagueti") que son muy difíciles de depurar y pueden contener errores. Para solucionar el problema en la Programacion Estructuradas existen estructuras de control que encierran un conjunto de instrucciones (con una instruccion al principio y otra al final) y lo que hacen es ejecutar el bloque de instrucciones entero un número determinado de veces, mientras se cumpla una condicion o hasta que se cumpla una condicion, según sea la estructura. A estas estructuras también se las conoce como "Bucles" o "Lazos".

 

1.9.2 - Como detener un programa

Al usar estas estructuras nos podemos encontrar con el problema de que si el programa no está escrito correctamente nunca se salga de la estructura de control produciéndose el efecto llamado "Bucle infinito" que puede llegar a bloquear el ordenador.

En un programa normal ya compilado y terminado que se ejecute bajo Windows puede pasar que el ordenador se bloquee y aparezca una pantalla azul recuerdo de Bill Gates de tipo "El sistema está ocupado o no responde...", con lo que casi seguro que vamos a tener que reiniciar el ordenador.

En los entornos de programacion esto normalmente no llegará a ocurrir. En caso de que nuestro programa se bloquee puede que se agoten los recursos del sistema y el programa se detenga dando un error de tiempo de ejecucion y volviendo al editor de codigo.

Si el programa se queda bloqueado se puede pulsar la siguiente combinacion de teclas:

 

Control + Pausa

 

para detener la ejecucion del programa y volver al editor de codigo donde habrá que repasar el codigo para que esto no ocurra y el programa funcione siempre bien. En algunos casos tras pulsar esta combinacion de teclas habrá que pulsar una vez la tecla ENTER para desbloquear el programa.

 

1.9.3 - Instruccion FOR...NEXT

 

Empecemos con un ejemplo como siempre. Vamos a escribir un programa que escriba los números del 1 al 5 usando las instrucciones que ya conocemos.

 

CLS

PRINT 1

PRINT 2

PRINT 3

PRINT 4

PRINT 5

 

Como se puede ver es un programa bastante tonto. Hay cinco instrucciones casi iguales. Solo cambia el valor de la expresion que cada vez vale lo que en la instruccion anterior más uno, por lo tanto también podíamos haber hecho esto:

 

CLS

n = 0

n = n + 1

PRINT n

n = n + 1

PRINT n

n = n + 1

PRINT n

n = n + 1

PRINT n

n = n + 1

PRINT n

 

Se puede comprobar que el resultado es el mismo que en el programa anterior y ahora sí que tenemos cinco pares de instrucciones completamente idénticos. Vamos a hacer el mismo programa con la nueva instruccion FOR ("Para" en castellano):

 

CLS

FOR n = 1 TO 5

            PRINT n

NEXT

 

Ya está. Mira que sencillo, pero ahora viene la explicacion.

Esto lo que hace es que se ejecute lo que hay entre el FOR y el NEXT cinco veces siguiendo estos pasos:

•          La primera vez n vale 1, como pone en la instruccion.

•          Se ejecuta el bloque de instrucciones con n valiendo 1

•          AUTOMÁTICAMENTE n se incrementa en 1, pasando a valer 2

•          Se comprueba que n es menor o igual que 5, y como lo es se sigue.

•          Se ejecuta el bloque de instrucciones con n valiendo 2

•          AUTOMÁTICAMENTE n se incrementa en 1, pasando a valer 3

•          Se comprueba que n es menor o igual que 5, y como lo es se sigue.

•          Se ejecuta el bloque de instrucciones con n valiendo 3

•          AUTOMÁTICAMENTE n se incrementa en 1, pasando a valer 4

•          Se comprueba que n es menor o igual que 5, y como lo es se sigue.

•          Se ejecuta el bloque de instrucciones con n valiendo 4

•          AUTOMÁTICAMENTE n se incrementa en 1, pasando a valer 5

•          Se comprueba que n es menor o igual que 5, y como lo es se sigue.

•          Se ejecuta el bloque de instrucciones con n valiendo 5

•          AUTOMÁTICAMENTE n se incrementa en 1, pasando a valer 6

•          Se comprueba que n es menor o igual que 5, y como ya no lo es se sale del bucle y se ejecuta la siguiente instruccion que venga detrás del NEXT.

 

Todo esto puede parecer muy complicado, pero con la práctica conseguiremos que esta sea una de las instrucciones más fáciles de entender de la programacion, solo habrá que detenerse a pensar en estos pasos cuando algún programa no haga lo que queremos y no demos con el error.

Veamos la sintaxis de la instruccion FOR:

 

FOR contador = inicio TO final

            bloqueInstrucciones

NEXT

 

contador es la variable que usaremos como contador (el FOR la modifica automáticamente) y tendrá que ser de tipo numérico, normalmente entero aunque también puede ser real. Ya hemos hablado de los contadores en el tema de los operadores de asignacion.

inicio es una expresion numérica cuyo valor tomará el contador la primera vez.

final es una expresion numérica cuyo valor lo usará el FOR de forma que solo entrará si el contador no supera al valor de esta expresion. En nuestro ejemplo el final era 5 y cuando el contador (n) llegaba a valer 6 ya no entrábamos.

 

Ahora vamos a ver dos normas muy importantes que hay que seguir siempre con los contadores de los FOR

 

•   No debemos modificar el valor de esta variable dentro del bucle, ya lo hace automáticamente la instruccion FOR. Dicho de otra forma: No debemos asignar ningún valor a esta variable hasta después de terminar el FOR.

•   Una vez terminado el FOR no debemos leer el valor de la variable contador porque su valor queda indeterminado. Podremos usar esta variable más adelante si previamente le asignamos un valor antes de intentar leerla.

Estas normas nos las podríamos saltar sin dar un error de ejecucion, pero puede que el mismo algoritmo de distintos resultados en distintas versiones de BASIC, ya que el contador es manejado internamente por el intérprete del lenguaje de programacion y puede que no siempre se haga de la misma forma.

 

Los valores inicio y fin no tienen por que ser expresiones constantes. En este ejemplo escribiremos los números desde uno hasta donde quiera el usuario:

 

CLS

INPUT "Escribe hasta donde quieres llegar: ", max

FOR n = 1 TO max

            PRINT n

NEXT

 

No es necesario que tengamos que usar siempre el valor del contador para calcular algo. Este FOR escribe "Practica en Laboratorio" siete veces:

 

FOR n = 1 TO 7

            PRINT "Practica en Laboratorio"

NEXT

 

Y este hace exactamente lo mismo:

 

FOR n = 82 TO 88

            PRINT "Practica en Laboratorio"

NEXT

 

El siguiente escribe los pares del 2 al 10, es decir, 2, 4, 6, 8, 10.

 

FOR n = 1 TO 5

            PRINT n * 2

NEXT

 

En QBasic hay una forma de hacer esto más fácilmente:

 

FOR n = 2 TO 10 STEP 2

            PRINT n

NEXT

 

Antes veíamos que el FOR incrementa automáticamente al contador en 1 en cada pasada. Usando la palabra clave STEP seguida de una expresion numérica conseguimos modificar este incremento.

 

Otro ejemplo con STEP que se explica solo.

 

CLS

INPUT "Escribe un número: ", s

PRINT "Estos son los números del 0 al 100 de "; s; " en "; s

FOR n = 0 TO 100 STEP s

            PRINT n

NEXT

 

Todo esto funciona muy bien, espero que se entienda. Pero puede surgir una duda, supongamos que escribimos el número 7 y el programa escribe de siete en siete, dando este resultado:

 

Escribe un número: 7

Estos son los números del 0 al 100 de 7 en 7

0

7

14

21

28

35

42

49

56

63

70

77

84

91

98

 

Como se puede ver, no se ha alcanzado el 100 es porque el siguiente valor que sería el 105 ya supera al 100 que es valor final del FOR y no se ejecuta.

 

También puede ocurrir que la expresion del STEP valga 0. En este caso el FOR incrementará en cero el contador con lo que nunca se llegará al valor final y se producirá un bucle infinito. Habrá que pulsar Ctrl+Pausa para detener el programa y corregir el codigo.

 

Ahora ya podemos hacer que un FOR funcione hacia atrás, escribiendo la expresion final menor que la inicial y una expresion negativa en el STEP. Como ejemplo un FOR que escriba los números del 10 al 1.

 

FOR n = 10 TO 1 STEP -1

            PRINT n

NEXT

 

Si no usamos el STEP negativo y escribimos el valor final menor que el inicial, nunca se ejecutará el bloque FOR. Si un programa no funciona bien porque un FOR no se ejecuta nunca será conveniente revisar esto.

 

Como siempre, dentro del bloque FOR puede ir cualquier tipo de instrucciones, incluido otro FOR. Veamos un ejemplo:

 

FOR i = 1 TO 8

            FOR j = 1 TO 5

                        PRINT "Hola"

            NEXT

NEXT

 

¿Cuantas veces escribirá "Hola" este programa? Si el FOR interior se ejecuta entero 8 veces y cada vez escribe "Hola" 5 veces, en total lo hará 8 por 5 igual a 40 veces, es decir, el producto.

 

Este tipo de instrucciones son especialmente útiles en algoritmos que ya veremos más adelante como el recorrido de matrices.

 

Hay que tener cuidado de no usar la misma variable contador para los dos FOR, ya que romperíamos la regla de no modificar el valor del contador del primer FOR y el programa no funcionaría bien.

 

QBasic permite escribir a continuacion del NEXT el nombre del contador del FOR, por ejemplo:

 

FOR i = 1 TO 8

            FOR j = 1 TO 5

                        PRINT "Hola"

            NEXT j

NEXT i

 

Esto puede ser útil para saber en un listado muy complicado a que FOR corresponde cada NEXT, pero si encolumnamos correctamente nuestro programa esto no será necesario.

 

El FOR en Basic es bastante flexible. En otros lenguajes funciona de otra forma o incluso puede que ni siquiera exista, ya que como veremos a continuacion no es imprescindible para construir un algoritmo.

 

TEMA 1.10

ESTRUCTURA DE REPETICION WHILE...WEND

 

En este tema vamos a ver una estructura repetitiva más primitiva que el PARA ya que no maneja automáticamente el contador y por lo tanto es más difícil de utilizar, pero usada correctamente puede ser bastante más flexible.

 

Recordemos el ejemplo de escribir los números del 1 al 5 con la instruccion FOR.

 

FOR n = 1 TO 5

            PRINT n

NEXT

 

Ahora veremos como se hace lo mismo en QBasic usando la instruccion WHILE (Mientras).

 

n = 1

WHILE n <= 5

            PRINT n

            n = n + 1

WEND

 

Esto lo que hace es ejecutar el bloque de instrucciones (Lo que hay entre el WHILE y el WEND) una y otra vez mientras se cumpla la condicion del WHILE. Un poco más difícil que con el FOR. Vamos a verlo paso a paso:

 

Usaremos como contador la variable n y por tanto la tenemos que inicializar nosotros al valor que queramos que tenga la primera vez, en este caso 1.

 

Se comprueba la condicion del WHILE: como n vale 1 que es menor o igual que 5, entramos.

Se ejecutan las instrucciones del bloque con n valiendo 1. La última instruccion incrementa n en 1, con lo que pasa a valer 2.

Volvemos al WHILE y se comprueba su condicion: como n vale 2 que es menor o igual que 5, entramos.

 

Se ejecutan las instrucciones del bloque con n valiendo 2. La última instruccion incrementa n en 1, con lo que pasa a valer 3.

Volvemos al WHILE y se comprueba su condicion: como n vale 3 que es menor o igual que 5, entramos.

 

Se ejecutan las instrucciones del bloque con n valiendo 3. La última instruccion incrementa n en 1, con lo que pasa a valer 4.

Volvemos al WHILE y se comprueba su condicion: como n vale 4 que es menor o igual que 5, entramos.

 

Se ejecutan las instrucciones del bloque con n valiendo 4. La última instruccion incrementa n en 1, con lo que pasa a valer 5.

Volvemos al WHILE y se comprueba su condicion: como n vale 5 que es menor o igual que 5, entramos.

 

Se ejecutan las instrucciones del bloque con n valiendo 5. La última instruccion incrementa n en 1, con lo que pasa a valer 6.

Volvemos al WHILE y se comprueba su condicion: como n vale 6 que ya no es menor o igual que 5, no entramos y pasamos a la siguiente instruccion que haya detrás del WEND.

 

Se puede ver que el funcionamiento es parecido al del FOR, solo que aquí lo tenemos que controlar todo nosotros. Las dos reglas que dijimos sobre los contadores del FOR ya aquí no tienen sentido porque de hecho nosotros vamos a tener que incrementar el contador haciendo una asignacion y una vez terminado podemos estar seguro del valor que tiene la variable.

 

Una norma que sí conviene respetar (Aunque no siempre es necesario) es que la instruccion que incrementa el contador sea la última del bloque, ya que si está en otro sitio ejecutaremos unas instrucciones con un valor y las demás con el otro, con lo que nos podemos liar. Un error muy típico es que se nos olvide de poner la instruccion de incrementar el contador, produciendo un bucle infinito que hará que nuestro programa no termine nunca. Si un programa se bloquea es conveniente revisar esto.

También puede pasar que no lleguemos a entrar al MIENTRAS porque la condicion ya sea falsa la primera vez, por ejemplo:

 

contador = 120

WHILE contador < 100

            PRINT "Esto no se va a llegar a escribir nunca.”

            contador = contador + 1

WEND

 

Hasta ahora hemos hablado de contador, pero como veremos en los ejemplos podemos usar un acumulador, o ninguno de los dos, ya que la condicion del WHILE puede ser cualquiera y no hay porqué contar ni acumular algo siempre.

 

Veamos algunos ejemplos de MIENTRAS:

Rutina que escribe del 0 al 100 de 2 en 2:

c = 0

WHLE c <= 100

            PRINT c

            c = c + 2

WEND

 

Escribir de 50 hasta 1 hacia atrás:

 

c = 50

WHILE c >= 1

            PRINT c

            c = c - 1

WEND

 

Calcular el factorial de un número que pedimos al usuario (Este ejemplo se mejorará en otro tema más adelante):

INPUT "Escribe un número para calcular su factorial: ", num

 

c = 1

factorial = 1

WHILE c <= num

            factorial = factorial * c

            c = c + 1

WEND

PRINT "El factorial de”; num; "es”; factorial

Leer números por teclado hasta que se escriba el 0:

INPUT "Escribe números (0 para salir):”, num

WHILE num <> 0

            INPUT "Escribe números (0 para salir):”, num

WEND

 

Este último ejemplo presenta lo que se conoce como lectura anticipada. Antes de llegar al WHILE hemos tenido que conseguir el valor de la variable num porque si no lo hacemos puede pasar que num valga cero y por lo tanto no lleguemos a entrar al bucle. Esto es útil en casos como la lectura de ficheros secuenciales, pero otras veces conviene evitarlo para no repetir instrucciones. Veremos como hacerlo en el siguiente tema.

 

Una última cosa es que hay una teoría en informática que dice que cualquier algoritmo puede ser programado usando solamente instrucciones MIENTRAS. Es decir, ni bloques IF, ni ELSE, ni CASE, ni FOR, ni otras estructuras que veremos más adelante. Yo no lo he comprobado, pero si a alguien le ha gustado mucho este tema ya puede empezar a hacerlo.

 

Tema 1.11

ESTRUCTURA DE REPETICIoN DO...LOOP

1.11.1 - ESTRUCTURA DO...LOOP

Esta estructura es similar a la WHILE, solo que la condicion se especifica al final del bloque, con lo que puede ser más fácil de entender y nunca tendremos que hacer "lecturas anticipadas" ya que siempre se entra por lo menos una vez.

La principal novedad de este bloque es que podemos repetir las instrucciones MIENTRAS se cumpla la condicion o HASTA que se cumpla.

El siguiente ejemplo escribe los números del 1 al 5 usando una instruccion DO...LOOP WHILE que hace que el bucle se ejecute MIENTRAS nuestro contador sea menor que 5.

 

n = 0

DO

            n = n + 1

            PRINT n

LOOP WHILE n < 5

 

Observa que el contador se incrementa al principio del bucle, y por lo tanto la primera vez que escribamos n, ya tendrá el valor de 1. La última vez escribirá 5 y al llegar a la condicion se comprobará que NO es menor que 5 y ya salimos.

Ahora haremos lo mismo con la instruccion DO... LOOP UNTIL que ejecutará el bloque HASTA QUE el contador llegue a valer 5.

 

n = 0

DO

            n = n + 1

            PRINT n

LOOP UNTIL n = 5

 

Esto es parecido. Observa que la condicion es justo la contraria. La última vez n vale 5 y después de escribirla se comprueba que la condicion es verdadera y se sale del bucle.

 

Las instrucciones DO...LOOP UNTIL son normalmente las más fáciles de comprender. En la ayuda de QBasic recomiendan que se dejen de usar las instrucciones WHILE...WEND para usar mejor las DO...LOOP, pero algunas veces será mejor usar las WHILE...WEND como en el caso de los ficheros secuenciales que ya veremos más adelante.

 

1.11.2 - DEPURACION DE DATOS DE ENTRADA

Ahora ya estamos en condiciones de ver una forma de conseguir que un programa que requiere la intervencion del usuario para introducir datos de entrada no avance HASTA QUE el usuario no escriba los datos correctos.

En el tema de Entrada/Salida vimos que QBasic es capaz de controlar que no se metan valores fuera de rango, por ejemplo que si el programa pide un entero no se pueda escribir un numero mayor de 32767. Aquí lo que vamos a ver es como conseguir que se pedimos un mes el programa no avance hasta que el usuario escriba un número entre 1 y 12. Esto lo deberíamos hacer en cualquier programa siempre que pidamos al usuario que escriba algo.

 

Para hacer este control lo que hacemos es meter la instruccion INPUT dentro de un bloque REPETIR, del que no salimos HASTA que la respuesta sea correcta o MIENTRAS sea incorrecta.

 

Vamos a ver unos ejemplos que aclararán todas las posibles situaciones.

Leer un número menor que 100

 

CLS

DO

            INPUT "Escribe un número menor que 100: ",num

LOOP UNTIL num < 100

 

Aquí no seguimos hasta que el número sea menor que 100. En el siguiente ejemplo seguiremos repitiendo la pregunta mientras el número sea mayor o igual que 100

 

CLS

DO

            INPUT "Escribe un número menor que 100: ",num

LOOP WHILE num >= 100

 

Puedes volver a comprobar aquí que para el mismo problema la condicion del LOOP WHILE es justo la inversa a la del LOOP UNTIL.

Ahora pedimos un mes, que tiene que ser entre 1 y 12

 

CLS

DO

            INPUT "Escribe un mes (0 a 12): ",mes

LOOP UNTIL (mes >= 1) AND (mes <= 12)

En este caso tenemos un intervalo y por lo tanto hay que controlar dos condiciones que uniremos con el operador AND, con lo que no seguimos HASTA que la primera se cumpla Y la segunda también.

También lo podíamos haber hecho con un bloque DO...LOOP WHILE

 

CLS

DO

            INPUT "Escribe un mes (0 a 12): ",mes

LOOP WHILE (mes < 1) OR (mes > 12)

 

Ahora no salimos MIENTRAS alguna de las dos condiciones se cumpla, son justo las contrarias a la del ejemplo anterior, ya que usamos el operador OR que también se puede decir que es el contrario al AND.

 

Por último vamos a ver un problema muy típico: Un programa que nos pide una clave de acceso para poder seguir. Este es el caso más sencillo en el que no pasamos hasta que no demos con la clave, otros problemas más complicados serían que el programa terminara tras varios intentos fallidos o que admitiera varias claves llévándonos según la que sea a una parte del programa.

 

CONST ClaveCorrecta$="Ábrete Sésamo"

CLS

DO

            INPUT "Escribe la clave: ",clavePrueba$

LOOP UNTIL clavePruebas = claveCorrecta$

PRINT "Ya has entrado"

 

Recordar también que en un programa terminado para explotacion la clave nunca va a estar en el listado del programa, sino que será obtenida de algún tipo de fichero o base de datos para que el usuario tenga la posibilidad de cambiarla.