Revistas en papel
Anterior
MenĂº
Logotipo

Amiga.InFo Nº 1 - Enero/Febrero 1995 - AMOS

NOTA: Pulsando sobre las fotos con borde azul ampliarás la imagen.

Tutorial de
AMOS
      Procedimientos.
En este artículo, R. Romero explicará las diferentes soluciones de las que dispondremos para hacer los listados de nuestros programas más legibles y comprensibles.

Todo lo dicho aquí se podrá tomar también como un esquema de trabajo a seguir, aunque reconozco que a veces no es posible.

Procedimientos

Empezaré explicando qué son los procedimientos (Procedure en AMOS) y para qué sirven. Muchos usuarios que disponen o han tenido Amigas 500 y 2000, usaron en su momento el intérprete de Microsoft Amiga Basic. Aunque no era un lenguaje muy "versátil", por no decirle otros adjetivos, tenía de algunos aciertos: además de los típicos gotos y gosub de todos los intérpretes basic incorporaba subprogramas. Los subprogramas eran partes del listado que estaban aisladas del resto del programa y se comunicaban con el programa principal mediante variables que transferían los resultados. Mucha gente se preguntará para qué puede servir ésto. Imaginemos por un momento que hemos hecho un programa que nos genera polígonos en 3D y los rota en tiempo real. Nuestro programa sería algo así:

El cual parece muy claro. Pero ahora imaginemos que desarrollamos una subrutina más potente y rápida que la anterior y queremos cambiarla. Lo más seguro que nos ocurrirá es que tengamos que modificar gran parte del programa, puesto que nuestra subrutina ROTAR3D utiliza variables que son modificadas por el programa principal. Claro que, también podríamos tener unas variables para el programa principal y otras para las subrutinas, por ejemplo:

Lo cual parece lógico, pero hemos complicado en gran medida la inicialización de nuestro programa, y además, no hemos resuelto totalmente nuestro problema. Aún hay que comunicar la subrutina con el programa principal en la mayoría de los casos.

Llegados a este punto, ya se entiende que los procedimientos de AMOS (y los subprogramas en Amiga Basic) son la solución que buscamos. Un procedimiento es una parte del programa que tiene sus propias variables y es totalmente independiente del programa principal. Los procedimientos se comunican con el programa principal mediante variables globales, variables compartidas o resultados en funciones.

Los procedimientos constan de Cabecera, Inicialización (si es necesaria), Cuerpo y Final. Podemos encontrarnos con los siguientes:

1.- Cabecera sin parámetros y no devuelve resultados

Procedure ROTAR3D
...
End Proc

2.- Cabecera con parámetros y no devuelve resultados

Procedure ROTAR3D [X3D#,Y3D#,Z3D#,ANGULO#]
...
End Proc

3.- Cabecera sin parámetros y devuelve resultados

Proceduce ROTAR3D
...
End Proc[RESULTADO]

4.- Cabecera con parámetros y devuelve resultados

Procedure ROTAR3D [X3D#,Y3D#,Z3D#,ANGULO#]
...
End Proc[RESULTADO]

Las variables que usamos como parámetros son copias. No modifican el valor de las variable "originales".

Para usar un procedure que hemos declarado, simplemente hacemos (usando el ejemplo):

Proc ROTAR3D

O si tiene parámetros:

Proc ROTAR3D [X3D#,Y3D#,Z3D#,ANGULO#]

También podemos usar el procedure sin poner la palabra clave "Proc" delante, pero éste puede llevar a confusión. Por ejemplo:

ROTAR3D : Print "Se acabó" ROTAR3D: Print "Se acabó"

El primer caso sería considerado pro el intérprete como una llamada al procedimiento ROTAR3D y luego daría el mensaje "Se acabó" por pantalla. En cambio, el segundo caso sería considera como una etiqueta que se llama ROTAR3D y no ejecutaría nuestro procedimiento. Las etiquetas son usadas por los comandos Goto y Gosub para saltar.

Si hemos declarado que nuestro procedimiento tiene un número N de parámetros, cuando lo usemos, tenemos que pasarle exactamente N parámetros. Si no, el intérprete nos dará un error del tipo "Syntax Error".

Si nuestro procedimiento devuelve un resultado, lo podemos recibir en el programa principal mediante las instrucciones Param. Hay tres tipos de instrucciones Param, una para cada tipo básico de variable.

Para variables booleanas y numéricas enteras (recordad, son lo mismo)

Proc MIFUNCION
a=Param

Para variables numéricas reales:

Proc MIFUNCION
a#=Param#

Para variables de tipo cadena:

Proc MIFUNCION
a$=Param$

Por cierto, si nuestro procedimiento devuelve un resultado, no estamos obligados a recogerlo. Es más, si devuelve un resultado de tipo cadena y nosotros lo queremos interpretar de otro tipo, podemos hacerlo.

No se pueden declarar procedimientos dentro de otro procedimiento.


Variables

Bueno, de momento tenemos nuestros procedimientos y sabemos que pueden variar su función dependiendo de unos parámetros de entrada, además de poder devolvernos UN SOLO RESULTADO. Sí, por desgracia, los procedimientos sólo pueden devolver un resultado. Si queremos más resultados, tenemos que utilizar variables globales o de intercambio.

A) Variables Globales.-

Las variables globales son aquellas que se declaran, normalmente, al principio del programa como tales con el comando Global.

Ejemplo

Global X3D,Y3D,Z3D

Nótese que, si inicializamos antes una variables y la declaramos como global, lo que tendremos en realidad es una constante global, lo cual puede ser muy interesante. Ejemplo:

_MAXIMO=100 : Rem El símbolo "_" es para que el intérprete no lo confunda
Global _MAXIMO : Rem con la palabra clave Max

Para declarar una matriz como global, tiene que ser declarada previamente:

Dim A(10) Global A()

¡Atención! No olvidar los paréntesis al declarar la matriz como global, si no, nos estaríamos refiriendo a la variable A.

B) Variables de intercambio.-

Las variables de intercambio o compartidas, se declaran en la primera línea del Cuerpo de nuestro procedimiento con el comando Shared. Ejemplo:

Este programa nos calcula X^2+Y^2 e incrementa en uno las variables X e Y desde fuera del programa principal.

¿Por qué existen variables globales y variables compartidas? En principio, parece que sirven para lo mismo y así es. El motivo es la eficiencia del programa y la velocidad. Cuantas más variables globales tenga un programa, consumirá más memoria y será más lento. Además, en listados de considerable tamaño, es muy posible que perdamos el control total sobre qué variables globales hemos declarado y esto pueda llevar a producir errores. Ejemplo:

Global A
...
Rem Muchas líneas de listado
...
Rem Muchas más
...
Proc MIFUNCION
...
If A<100 THEN Proc LOQUESEA
...
End Procedure MIFUNCION
Rem Aquí usamos la variable A pero no
Rem nos acordamos de que ya está siendo
Rem usada por el programa principal.
...
A=100
...
End Proc

Si no tenemos cuidado, es posible que tengamos sorpresas desagradables. Otro motivo es que, cuando declaramos variables globales, el AMOS tiene que pasar toda esa información a los procedimientos en cada llamada y es un proceso muy lento y costoso de memoria.

Yo recomiendo que usemos, siempre que sea posible, Param y Shared en lugar de Global, además de poner nombres explicativos a las variables.

Para aquellos que conozcan Modula2 o Pascal he aquí un ejemplo de equivalencia entre los procedimientos de estos lenguajes de programación y los de AMOS, primero la versión en Pascal:

function mifuncion(x : integer; var y : integer):boolean;
var b : boolean;
...
return (b);
end mifuncion;

Y ahora la versión en AMOS:

Procedure MIFUNCION[X]
Shared Y
...
End Proc[B]

Nótese que las variables declaradas tras la palabra clave Shared son equivalentes a si fueran declaradas como paso de parámetros de entrada por referencia en la versión de Pascal. Por otra parte, en la versión de AMOS, no nos hace falta declarar previamente la variable B de tipo booleano.


Programación Estructurada

Si queremos hacer un programa estructurado en AMOS, recomiendo el siguiente esquema:

Primero.- Hacemos un esquema de nuestro programa principal.

Segundo.- Declaramos todas las cabeceras y finales de procedimientos que sean necesarios.

Ejemplo:

Rem Programa principal
...
End Rem Lista de procedimientos

Procedure MIFUNCION1 End Proc

Procedure MIFUNCION2 End Proc

Tercero.- Hacemos nuestro programa principal.

Cuarto.- Ponemos los parámetros que sean necesarios en los procedimientos que hemos declarado previamente. Si es necesario, creamos nuevos procedimientos. Terminamos de programar las procedimientos.

Nota: No he hecho referencia a la declaración de variables, pero parece lógico que la hagamos en los pasos tercero y cuarto ¿no?.

Otra nota: No recomiendo el uso de variables globales.


Matrices y procedimientos

Hemos visto como pasamos parámetros y recibimos resultados de los procediminetos en AMOS, pero sólo para tipos de datos "simple". Las matrices son tratadas de otra manera.

No se puede pasar una matriz como parámetro y tampoco se puede devolver como resultado. Lo que sí está permitido es usar un elementos de la matriz como parámetro y recibirlo como resultado.

Ejemplos:

Procedure MIFUNCION[A()]:
Rem No permitido

Procedure MIFUNCION[A(2)]:
Rem Correcto, pero así sólo tenemos acceso
Rem al elemento 2 de la matriz A

Análogamente para los resultados. No podemos recoger una matriz como resultado con una instrucción Param.

Si queremos usar las matrices en nuestros procedimientos, tenemos que hacerlo mediante los comandos Global o Shared.

Toda matriz que declaremos dentro de un procedimiento, será una matrix local y no será accesible por el programa principal.


Últimas Anotaciones

Cuando nos referimos a variables globales y compartidas, tenemos que usar el mismo nombre de variable. Sin embargo, en las variables locales no es necesario.

Mi orden de preferencia para trabajar es: primero las variables locales, luego las compartidas y por último las globales, si no hay más remedio.

Todas las variables locales que usemos dentro de un procedimiento perderán su valor al salir de éste. Si queremos conservar algún valor, ya sabéis, a usar alguna de las posibles soluciones que utilizan en el ejemplo y que he explicado.

Y ahora pasemos a comentar el listado de ejemplo. En él se hace un uso bastante extensivo de todo lo explicado en este capítulo. He incluido procedimientos de todo tipo y creo que su cometido está bastante claro.

A) Primero inicializamos diversas variables, así como la pantalla.
B) Luego llamamos a unos procedimientos para dibujar en pantalla diversos cuadrados.
C) Después dibujamos unos botones y sus textos.
D) Al final tenemos un bucle donde testeamos el estado de nuestros botones.

En este programa, se usa una serie de comandos que son únicos de AMOS y los comento ahora brevemente, aunque comentaré más extensamente su uso en el futuro.

Reserve Zone NUM : Reserva un espacio de memoria para un número NUM de zonas.
Si no ponemos ningún número se inicializan todas las zonas.
Set Zone NUM,X1,Y1 to X2,Y2 : Define las coordenadas de la zona NUM
Centre A$ : Imprime un texto centrado en la línea actual.
Locate COLUMNA,FILA : Posiciona el cursor de textos en las coordenadas de texto COLUMNA,FILA.
Text COL,FILA,A$ : Imprime un texto en pantalla con coordenadas gráficas.
=Mouse Click : Devuelve el botón del ratón que ha sido pulsado.

0 : No ha sido pulsado ningún botón
1 : Botón izquierdo
2 : Botón derecho
3 : Los dos botones o botón del medio (si dispones de él)

=Mouse Zone : Devuelve el número de zona que está situado debajo del cursor de nuestro ratón. Si no está encima de ninguna zona devuelve 0.

Si habéis ejecutado el programa, notaréis que los procedimientos que se refieren a los "gadgets" parecen que funcionan igual, y así es. Pero hay otro detalle muy importante que nos hará decantarnos por uno o por otro tipo. Si nos fijamos los procedimientos GADGETBOOL GLOBAL[] y GADGET BOOLSHARED[], nos obligan a declarar en el programa principal las variables que necesitamos. Mientras que GADGET BOOL[] es totalmente independiente del resto del programa, sólo es necesario que recojamos el resultado y eso, si lo deseamos. Gracias a ésto, podemos cambiar totalmente GADGETBOOL[] siempre que respetemos los parámetros de entrada y el resultado, y también, gracias a ésto podemos hacernos una biblioteca de procedimientos independientes.

Bueno, pues según ésto ya tenéis aquí vuestros primeros procedimientos para vuestra biblioteca.

Proc GADGETBOOL [X,Y,ANCHO,ALTO,BOOL] Devuelve variables booleanas y redibuja el "gadget".

Proc _BOX20 [X,Y,ANCHO,ALTP] Dibuja un recuadro tipo WB 2.0

Proc _BOX20INV [X,Y,ANCHO,ALTO] Dibuja un recuadro tipo WB 2.0 invertido

Proc CAJA [X,Y,ANCHO,ALTO] Dibuja una caja tipo WB 2.0

De todas maneras, estos procedimientos no son perfectos ya que no están a salvo de errores (sobre todo CAJA[] ). ¡A ver si descubrís los fallos y los arregláis!


Figura 1. Como hemos visto es fácil imitar el aspecto de nuestro querido Workbench.

Figura 2... y alguna de sus funciones.
Rem Inicialización
RESULT_A=True
RESULT_B=True
GADGET=True
K=0
Z=0
Global RESULT_A
Screen Open 0,640,256,16,Hired
Rem Paleta de WB 2.0
Palette $687,0,$FFF,$66C
Curs Off
Cls 0
Reserve Zone 3
Print Cdown$
Centre "Ejemplos de cajas utilizando unos procedimientos."
Proc _BOX20[120,40,80,40]
Proc _BOX20INV[240,40,80,40]
Proc CAJA[360,40,80,40]
Locate 10,11
Centre "Ejemplos de gadgets, ¡PULSALOS!"
Set Zone 1,100,102 To 119,111
Set Zone 2,100,122 To 119,131
Set Zone 3,100,142 To 119,151
Proc GADGETBOOL [100,102,20,10,GADGET]
Ink 2,0
Text 140,110,"Esta gedget representa a las variables locales."
Proc GADGETBOOLGLOBAL [100,122,20,10]
Ink 2.0
Text 140,130,"Esta gadget representa a las variables globales."
Proc GADGETBOOLSHARED [100,142,20,10]
Ink 2,0
Text 140,150,"Esta gadget representa a las variables compartidas."
Locate 1,20
Centre "Pulse botón derecho del ratón para acabar"
K=Mouse Click
Z=Mouse Zone
While K<>2
If K=1 and Z=1
Proc GADGETBOOL [100,102,20,10,GADGET]
GADGET=Param
If GADGET
Ink 1,0
Text 560,110,"Activado"
Else
Ink 1,0
Text 560,110," "
End if
End if
If K=1 and Z=2
Proc GADGETBOOLGLOBAL [100,122,20,10]
If RESULT_A
Ink 1,0
Text 560,130,"Activado"
Else
Ink 1,0
Text 560,130," "
End If
End If
If K=1 and Z=3
Proc GADGETBOOLSHARED [100,142,20,10]
If RESULT_B
Ink 1,0
Text 560,150,"Activado"
Else
Ink 1,0
Text 560,150," "
End If
Enf If
K=Mouse Click
Z=Mouse Zone
Wend
Reserve Zone
End

Procedure GADGETBOOLGLOBAL [X,Y,ANCHO,ALTO]
Rem Ejemplo de uso de variables globales
RESULT_A=Not(RESULT_A)
If RESULT_A
Proc _BOX20[X,Y,ANCHO,ALTO]
Else
Proc _BOX20INV[X,Y,ANCHO,ALTO]
End If
End Proc

Procedure GADGETBOOLSHARED [X,Y,ANCHO,ALTO]
Rem Ejemplo de uso de variables compartidas
Shared RESULT_B
RESULT_B=Not(RESULT_B)
If RESULT_B
Proc _BOX20[X,Y,ANCHO,ALTO]
Else
Proc _BOX20INV[X,Y,ANCHO,ALTO]
End If
End Proc

Procedure GADGETBOOL [X,Y,ANCHO,ALTO,BOOL]
Rem Ejemplo de uso de variables locales
Rem Requiere que en el programa principal
Rem recuperemos el resultado con Param
BOOL=Not(BOOL)
If BOOL
Proc _BOX20[X,Y,ANCHO,ALTO]
Else
Proc _BOX20INV[X,Y,ANCHO,ALTO]
End If
End Proc[BOOL]
Procedura _BOX20 [X,Y,ANCHO,ALTO]
Rem Dibuja un cuadro estilo WB2.0
Ink 2
Polyline X,Y To X+ANCHO,Y
Ink 1
Polyline To X+ANCHO,Y To X+ANCHO,Y+ALTO To X,Y+ALTO
Ink 2
Polyline To X,Y
End Proc

Procedure _BOX20INV [X,Y,ANCHO,ALTO]
Rem Dibuja un cuadrado invertido de WB 2.0
Ink 1
Polyline X,Y To X+ANCHO,Y
Ink 2
Polyline To X+ANCHO,Y To X+ANCHO,Y+ALTO To X,Y+ALTO
Ink 1
Polyline To X,Y
End Proc

Procedure CAJA [X,Y,ANCHO,ALTO]
Proc _BOX20 [X,Y,ANCHO,ALTO]
Proc _BOX20INV [X+1,Y+1,ANCHO-2,ALTO-2]
End Proc


Envía esta página web a un amigo:
Esta opción está desactivada temporalmente, rogamos disculpen las molestias

Volver a la página anterior

Al menú principal