BUCLES y SUBRUTINAS.
En este artículos presento los bucles y el manejo de las subrutinas, con esto simplificamos
el manejo del control del programa, sin usar tantas sentencias
GOTO.
BUCLES FOR
Una vez hemos repasado las sentencias
IF y
GOTO, que son las básicas
para el manejo del programa, en esta entrada veremos dos que nos simplificarán su
manejo, haciendo que el propio programa realice el control.
Un bucle es un trozo del programa que se ejecuta repetidamente, al final del bucle
una sentencia
GOTO nos retorna al principio, y en cualquier momento podemos
salir de él usando otros
GOTO. Vamos a hacer un bucle que nos calcule el
factorial de un número, sabemos que el
factorial de un número (que se marca con
el símbolo de admiración) es el producto de los números entre 1 y el número que
estamos calculando, así el
factorial de 5 es:
5! = 5.4.3.2.1 = 120
100 REM --------------------------------------------------------
110 REM - Calculo de factoriales, JAVaqué‚ 2012
-
120 REM --------------------------------------------------------
130 CLS: INPUT "Número: ";N
:REM Pedimos el número mero
140 IF (N>=0) THEN GOTO 170
:REM No para negativos
150 PRINT "ERROR: No se puede calcular el factorial de un negativo"
160 GOTO 240
:REM Ir al final
170 LET R=1
:REM R será el resultado
180 IF (N=0) THEN GOTO 230
:REM El factorial de 0 es uno
190 LET S=1
:REM En S lo que multiplicamos
200 LET R=S*R
:REM Multiplicamos anterior
210 LET S=S+1
:REM Sumamos 1 al número
220 IF (S<=N) THEN GOTO 200
:REM Mientras no sea mayor
230 PRINT ,N;"! = ";R
:REM El resultado
240 PRINT : PRINT
:REM Dejamos línea en blanco
250 INPUT "¿Otro? (S/N): ";O$
:REM Preguntamos si seguimos
260 PRINT : PRINT
:REM Dejamos línea en blanco
270 IF (O$="S") OR (O$="s") GOTO 130 :REM Si es si seguimos
280 IF (O$="N") OR (O$="n") GOTO 310 :REM Si es no salimos
290 PRINT "Responda S o N" : GOTO 240 :REM Repetir pregunta
310 PRINT "Fin del programa"
La parte marcada
en verde es la inicialización de las
variables necesarias, en R tendremos el resultado, lo empezamos en uno ya que lo
usamos multiplicando (si fuera sumando empezaríamos en cero), en S ponemos un contador
hasta el número, empezamos a contar en uno. Luego
en rojo el
bucle, multiplicamos el resultado anterior por el contador del número, le
sumamos uno al número, y se repite mientras no lleguemos a superar el número. Para
entender mejor un bucle se usan diagramas de valores de variables, en el que se
expresa como van cambiando las variables del programa a lo largo del tiempo, con
un valor de ejemplo:
Paso |
Valor de N |
Valor de R |
Valor de S |
130 INPUT N
<Se pide el número> |
5 |
Indeterminado |
Indeterminado |
170 LET R=1 |
5 |
1 |
Indeterminado |
180 IF (N=0) THEN GOTO 230
<NO es 0, comienza el bucle> |
5 |
1 |
Indeterminado |
190 LET S=1 |
5 |
1 |
1 |
PASO 1 POR EL BUCLE |
|
|
|
200 LET R=S*R |
5 |
1 |
1 |
210 LET S=S+1 |
5 |
1 |
2 |
220 IF (S<=N) THEN GOTO 200
<Es menos, seguir el bucle> |
5 |
1 |
2 |
PASO 2 POR EL BUCLE |
|
|
|
200 LET R=S*R |
5 |
2 |
2 |
210 LET S=S+1 |
5 |
2 |
3 |
220 IF (S<=N) THEN GOTO 200
<Es menos, seguir el bucle> |
5 |
2 |
3 |
PASO 3 POR EL BUCLE |
|
|
|
200 LET R=S*R |
5 |
6 |
3 |
210 LET S=S+1 |
5 |
6 |
4 |
220 IF (S<=N) THEN GOTO 200
<Es menos, seguir el bucle> |
5 |
6 |
4 |
PASO 4 POR EL BUCLE |
|
|
|
200 LET R=S*R |
5 |
24 |
4 |
210 LET S=S+1 |
5 |
24 |
5 |
220 IF (S<=N) THEN GOTO 200
<Es menos, seguir el bucle> |
5 |
24 |
5 |
PASO 5 POR EL BUCLE |
|
|
|
200 LET R=S*R |
5 |
120 |
5 |
210 LET S=S+1 |
5 |
120 |
6 |
220 IF (S<=N) THEN GOTO 200
<Es mayor, fin del bucle> |
5 |
120 |
6 |
Podemos simplificar esto usando la sentencia
FOR -
NEXT, que es el bucle usado en
el BASIC para realizar estas labores. Es un bucle automático, en el que una variable
va aumentando (o disminuyendo) su valor de forma automática. Veamos como cambiar
las 6 líneas anteriores por
170 LET R=1
:REM En R tendremos el resultado
180 IF (N=0) THEN GOTO 230 :REM El factorial de cero es uno
190 FOR S=1 TO N
:REM Bucle desde 1 hasta el valor de N
200 LET R=S*R
:REM Multiplicamos por el anterior
220 NEXT S
:REM Repetir el bucle
Es muy similar, pero nos ahorramos inicializar el valor de S e ir comparando, el
bucle se repite de forma automática, la sintaxis es
FOR variable = inicial
TO final
[
STEP suma] en el que usamos una variable por su nombre, que partirá de un valor
inicial, y se irá sumando el valor indicado por
STEP, hasta que se sobrepase al
valor final, momento en que se sale del bucle
Paso |
Valor de N |
Valor de R |
Valor de S |
130 INPUT N
<Se pide el número> |
5 |
Indeterminado |
Indeterminado |
170 LET R=1 |
5 |
1 |
Indeterminado |
180 IF (N=0) THEN GOTO 230
<NO es 0, comienza el bucle> |
5 |
1 |
Indeterminado |
190 FOR S=1 TO N |
5 |
1 |
1 |
PASO 1 POR EL BUCLE |
|
|
|
200 LET R=S*R |
5 |
1 |
1 |
220 NEXT S
<Es menos, seguir el bucle> |
5 |
1 |
2 |
PASO 2 POR EL BUCLE |
|
|
|
200 LET R=S*R |
5 |
2 |
2 |
220 NEXT S
<Es menos, seguir el bucle> |
5 |
2 |
3 |
PASO 3 POR EL BUCLE |
|
|
|
< 200 LET R=S*R |
5 |
6 |
3 |
220 NEXT S
<Es menos, seguir el bucle> |
5 |
6 |
4 |
PASO 4 POR EL BUCLE |
|
|
|
200 LET R=S*R |
5 |
24 |
4 |
220 NEXT S
<Es menos, seguir el bucle> |
5 |
24 |
5 |
PASO 5 POR EL BUCLE |
|
|
|
200 LET R=S*R |
5 |
120 |
5 |
220 NEXT S
<Es mayor, fin del bucle> |
5 |
120 |
6 |
Podemos usar
STEP para indicar decremento, usando un valor negativo de incremento
,
si queremos usar bucle que empiece por 10 y llegue hasta 1 decrementando de uno
en uno, usaremos
FOR i=10
TO 1
STEP -1.
Podemos usar
STEP para indicar que cuente de n en n, no tiene por qué ser uno, por
ejemplo sumaremos todos los números múltiplos de 2 menores de cien, usaremos un
bucle
FOR i=0
TO 99
STEP 2, lo que hará un bucle con los valores 0,0+2=2,
2+2=4, 4+2=6, etc. Si queremos multiplicar todos los números múltiplos de 13 menores
de 130 inclusive, podemos usar
FOR i=130
TO 0
STEP -13 (lo que dará 130, 117, 104,…,
13, 0) o bien
FOR i=0
TO 130
STEP 13 (lo que dará 0, 13, 26,…, 117, 130).
En versiones más modernas del BASIC existen otras instrucciones de control de bucle,
pero solo el
FOR está disponible en todas las versiones de BASIC de nuestras máquinas.
Subrutinas
La sentencia
GOTO hemos visto que transfiere la ejecución del programa a otra parte
del mismo, y si queremos regresar a donde estábamos debemos usar otro
GOTO. Para
evitar este mecanismo, podemos usar el manejo de subrutinas, que nos ayudará en
la definición de partes del programa reutilizables en otros programas, usando las
instrucciones
GOSUB y
RETURN.
Una subrutina es un trozo del programa que se ejecuta por si mismo, de manera independiente
del resto del programa, y que podemos llamar cuando queramos, devolviendo el control
al punto donde la hemos llamado de forma automática, sin necesidad de sabe desde
que punto la hemos llamado. Voy ha convertir nuestro programa de cálculo de factoriales
en una subrutina, y nos dará la diferencia entre el factorial
de dos números:
100 REM -------------------------------------------------------------
110 REM - Calculo de diferencia de dos factoriales, JAVaqué 2012
-
120 REM -------------------------------------------------------------
130 INPUT "Primer Número: ";N :REM
Pedimos el primer número
140 GOSUB 1000
:REM Llamamos a la subrutina
150 IF (R=-1) THEN GOTO 130
:REM No para negativos
160 LET N1=N : LET F1=R
:REM Guardamos el valor
170 INPUT "Segundo Número: ";N :REM Pedimos
el segundo número
180 GOSUB 1000
:REM Llamamos a la subrutina
190 IF (R=-1) THEN GOTO 130
:REM No para negativos
200 LET N2=N : LET F2=R
:REM Guardamos el valor
210 PRINT ,N1;"! (";F1;")- ";N2;"! (";F2;") = ";F1-F2
220 PRINT : PRINT
:REM Dejamos línea en blanco
230 INPUT "¨Otro? (S/N): ";O$ :REM
Preguntamos si seguimos o no
240 PRINT : PRINT
:REM Dejamos línea en blanco
250 IF (O$="S") OR (O$="s") GOTO 130 :REM Si es si seguimos
260 IF (O$="N") OR (O$="n") GOTO 280 :REM Si es no salimos
270 PRINT "Responda S o N": GOTO 220 :REM Si no es si o no repetir pregunta
280 PRINT "Fin del programa"
290 STOP
:REM Necesario para finalizar
1000 REM -------------------------------------------------------------
1010 REM - Subrutina de cálculo de un facTOrial.
-
1020 REM - Pasamos en N el número a calcular
-
1030 REM - Retorna en F el factorial, o -1 si no puede calcularlo
-
1040 REM -------------------------------------------------------------
1050 IF (N>=0) THEN GOTO 1090 :REM No
para negativos
1060 PRINT "ERROR: No se puede calcular el factorial de un negativo"
1070 LET R=-1
:REM Marcamos el error
1080 RETURN
:REM Salimos
1090 LET R=1
:REM En R tendremos el resultado
1100 FOR S=1 TO N
:REM Bule de 1 a N
1110 LET R=S*R
:REM Multiplicamos por el anterior
1120 NEXT S
:REM Bucle
1130 RETURN
:REM Salimos
Tras pedir el primer número, llamamos a la nueva subrutina en la línea en verde
usando GOSUB nro_de_linea, esto equivale a hacer GOTO 1000, y hacemos el cálculo
del número factorial. Si el número es negativo ponemos el resultado como -1 indicando
error y retornamos a la llamada usando la instrucción RETURN, que en este caso sería
como hacer un GOTO 150. Si el número es correcto, calculamos el factorial y al final
volvemos con otro RETURN, lo que nuevamente sería equivalente a hacer un GOTO 150.
Luego pedimos el segundo número, y llamamos nuevamente a la subrutina, lo que equivale
a hacer un GOTO 1000, pero cuando finalice la subrutina con el RETURN primero o
con el segundo, hará esta vez un GOTO 190, por lo que podemos usar la misma subrutina
desde dos partes de nuestro programa, sin preocuparnos de desde donde se llame,
cuando finalice continúa el programa por donde estaba, lo que es una gran ventaja,
ya que no hay que preocuparse de donde era ese lugar.
Solo existe una limitación, hay una pila de llamadas a subrutinas, en donde el programa
guarda la dirección de retorno de la misma, y esta pila tiene un tamaño limitado,
dependiendo de la versión del BASIC serán mas o menos, el mínimo creo que son 7,
por lo que no podemos usar llamadas anidadas a subrutinas en muchos niveles, para
evitar esto. Por otro lado, siempre que se pueda es mejor hacer un salto a una subrutina
que poner muchos GOTO, ya que mejora la legibilidad del programa, sobre todo pensando
que se puede salir de una subrutina desde cualquier parte, sin pensar a donde regresamos,
evitando muchos GOTO a la misma línea.
En esta entrada hemos aprendido a manejar los bucles FOR y las subrutinas. En la
próxima entrada seguiré con los bucles hablando de bucles anidados, y mejoraré el control del programa usando ON GOTO y ON GOSUB (que no son del primer BASIC pero
si están en casi todos), y mejoraré nuestra calculadora. Luego nos queda DEF
FN y READ/DATA para terminar el repaso de los comandos estándar. Animaros a teclear
estos programas y a idear nuevos, entenderlo bien, e intentar mejorarlo, solo se
aprende a programar programando.
Jose Antonio Vaqué Urbaneja, podéis contactar conmigo en
mi mail o ver más cosas en
mi blog