sábado, 29 de diciembre de 2012

Introducción a la programación usando BASIC (V): FOR y GOSUB


Artículo publicado en el número 5 de RetroWiki Magazine

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

No hay comentarios:

Publicar un comentario