jueves, 3 de noviembre de 2016

Programación del Sinclair QL (III): Procedimientos y funciones

Procedimientos y Funciones 

Hay lenguajes como el C en que estas dos estructuras de programación no se diferencian entre sí, mientras que en el superBASIC diferenciamos entre ambas según retornen o no un valor, siendo equivalentes el resto de sus características.

Ambas son estructuras recursivas, algo que en el BASIC estándar no existe como tal, aunque se puede hacer uso del GOSUB para conseguir cierta recursividad, es complejo por carecer de variables locales y las limitaciones del tamaño de la Pila de retorno lo hacen inviable en la mayoría de sistemas. Vemos un ejemplo muy sencillo de ambas:


BASIC     SuperBASIC
100 CLS
110 GOSUB 180
120 LET A=5:GOSUB 230:PRINT A
130 STOP
150 REM ---------------------
160 REM SALUDAR
170 REM ---------------------
180 PRINT "Hola a todos"
190 RETURN
200 REM ---------------------
210 REM INCREMENTAR
220 REM ---------------------
230 LET A=A+1
240 RETURN 
   
100 CLS
110 saluda
120 PRINT incremento(5)
130 REMark ------------------------
140 REMark PROCEDIMIENTO: Saludar
150 REMark ------------------------
160 DEFine PROCedure saluda
170   PRINT "Hola a todos"
180 END DEFine 
190 REMark ------------------------
200 REMark FUNCION:Incrementar
210 REMark ------------------------
220 DEFine FuNction incremento(a)
230   RETurn a+1
240 END DEFine 


En BASIC estándar hay que usar GOSUB para ir a otras partes del programa, para lo que hay que conocer el número de línea en que se encuentra nuestra rutina, y no olvidar el RETURN final pues si no el programa se liará bastante, y como todas las instrucciones son ejecutables, es necesario parar el programa para que no vuelva a ejecutar la primera rutina. En SuperBASIC podemos definir rutinas con nombre, y llamarlas desde cualquier punto del programa por ese nombre, esos trozos no se ejecutarán nunca por si solos, lo que hace que sean mas sencillos de usar.

Además opcionalmente podemos usar todos los parámetros de entrada que deseemos, cuando se llama se deben usar valores o variables para estos parámetros, que son copiados por valor a la rutina, estos parámetros ya no son variables globales sino que se comportan como variables locales en la rutina, por lo que se simplifica mucho el manejo de las variables, en nuestro ejemplo pasamos una variable a la función incremento, esta recibe el valor en una variable que siempre es local, lo que simplifica mucho el diseño de buenas rutinas, que no afecten al resto del programa.

Vemos que el procedimiento no tiene valor alguno de retorno, mientras que la función lo tiene siempre, es importante tenerlo en cuenta, ya que no podemos llamar por ejemplo a PRINT saluda pues no hay valor de retorno, ni llamar directamente a incrementa si no usamos el valor retornado en ningún lugar. En esto se diferencia de C en que se puede no usar el valor de retorno.

El paso de parámetro a nuestras rutinas siempre se realiza por valor, si deseamos modificar el valor de una variable debemos recurrir a usar variables globales para ello. Una función puede retornar solo un valor, pero el tipo de este valor no se define por lo que podemos asignar una variable de cadena a una entera, y esperar que el SuperBASIC nos resuelva la conversión del tipo de la variable. A mi particularmente me gustan los lenguajes fuertemente tipados que me evitan errores, pero en nuestro querido QL no hay otra opción.

Vamos con la recursividad, usando el ejemplo típico del factorial. Ritchie, uno de los creadores del C, dijo que era muy mal ejemplo pues la solución iterativa del factorial es mas rápida y eficiente que la recursiva, y prefería poner como ejemplo la impresión de un número en pantalla, ya entraremos en eso.

BASIC iterativo     SuperBASIC recursivo
100 CLS
110 INPUT "Valor: ";V
120 GOSUB 180
130 PRINT "El factorial es: ";F
140 END
150 REM ---------------------
160 REM FACTORIAL
170 REM ---------------------
180 LET F=1
190 FOR I= V TO 1 STEP -1
200   F=F*I
210 NEXT I
220 RETURN
   
100 CLS
110 INPUT "Valor: ";v
120 PRINT "El factorial es: ";factorial(v)
190 REMark ------------------------
200 REMark FUNCION: Factorial
210 REMark ------------------------
220 DEFine FuNction factorial(a)
230   IF (a = 0) THEN 
231     RETurn 1
232   ELSE 
233     RETurn a * factorial(a-1)
234   END IF 
240 END DEFine


Este ejemplo pone en claro que en caso del BASIC estándar, debemos conocer la dirección del programa en que se encuentra la rutina y los valores de variables que usará, mientras que en SuperBASIC nos vale con saber el nombre de la rutina. Si tenéis curiosidad y ejecutáis en un QL el programa para el valor 200, el resultado tardará un poco pero será 3.060575E614 presentando el resultado en notación científica, si ponéis en marcha un GWBASIC por ejemplo con el DOSBOX, el proceso iterativo dará un error de OverFlow para valores mayores a 33.

Otra característica muy útil es la posibilidad de usar variables locales, lo que evita muchos errores de programación, simplemente definiéndolas como primera instrucción de la función o el procedimiento:

100 b=10 : calculos: PRINT b
200 DEFine PROCedure calculos
210   LOCAL b
220   b = 20
230 END DEFine
 
Si ejecuta este programa tal cual, el valor que imprime es 10, pero si elimina la línea 210, el valor que mostrará será 20. Siempre todos los parámetros que reciba serán tratados como variables locales.

No hay comentarios:

Publicar un comentario