Clipper 5.x

Clipper a fondo, solo para entendidos
Por Diego Lucio D'Onofrio

 

(22/Mar) Primera entrega : El comando @...Get

Reseña antes de comenzar:
Por lo menos en las primeras entregas voy a hacer una pequeña explicación de ciertos comandos y métodos como para que vayan entendiendo a que apunta este tutorial.
En esta entrega por ser la primera me voy a explayar un tanto mas.
El comando "Clear" ó "Cls" ejecuta dos funciones, una para borrar la pantalla y otra para posicionar el cursor en la columna cero y fila cero de la pantalla.
Podría afirmarse que un comando de borrar la pantalla siempre está precedido de otro que posiciona el cursor en el lugar donde escribiremos un mensaje, dibujaremos un cuadro (BOX), ó haremos un "Get", por lo que borrar la pantalla usando "Clear" ó "Cls" desperdicia un comando como veremos en el siguiente ejemplo:

El siguiente programa "ejemplo (1)":
Clear
@ 12, 35 Say "Esto es una prueba"

Es interpretado por el compilador de la siguiente manera:

Scroll() ;
// Borra la pantalla
SetPos(0,0) ;
// Posiciona el cursor en la fila 0, columna 0
DevPos( 12, 35 )
// Posiciona el cursor en la fila 12, columna 35
DevOut( "Esto es una prueba" )
// Imprime el mensaje en la posición actual en el dispositivo de salida, en este caso la pantalla.

Como podrán apreciar hay una función SetPos() que mueve el cursor a (0,0), inmediatamente después se ejecuta la función DevPos() que vuelve a corregir la posición del cursor.
Para evitar esta redundancia en adelante usaremos la función Scroll() para borrar la pantalla siempre y cuando no necesitemos escribir o mostrar algo en la posición cero, cero.

El comando @...Say ejecuta internamente solo dos funciones como podemos apreciar en el ejemplo anterior, pero también puede ser optimizado ya que la función a la que llama para mostrar el mensaje es DevOut() la cual consume innecesariamente algunos ciclos de procesador cuando se utiliza para imprimir en la pantalla.
DevOut() evalúa cual es el dispositivo en curso antes de enviar los datos, en su lugar se puede usar QOut() que cumple la misma función solo que siempre dirige la salida al dispositivo primario (la pantalla).
La función DevPos() también puede ser reemplazada por SetPos(), la diferencia es mas que obvia, DevPos() mueve el cursor en el dispositivo en uso, ya sea la impresora, un archivo o la pantalla, en cambio cuando queramos mover el cursor de la pantalla SetPos() lo hará sin evaluar previamente nada.
En resumen el programa citado en el ejemplo (1) quedaría así:

Scroll()
SetPos( 12, 35 )
QOut("Esto es una prueba" )

Aunque parezca mentira hemos ahorrado casi un 40% de ciclos de procesador, esto es imperceptible en este ejemplo, pero si se trata de un bucle que ejecuta varios miles de veces estas líneas de código, la diferencia será claramente visible.

Es obvio que hay situaciones en las que no es conveniente reducir el código, por ejemplo el siguiente comando:

@ 10,10 Get cVar

Es interpretado por Clipper de la siguiente manera :
Ejemplo (2):

SetPos( 10, 10 )
AAdd( GetList, _GET_( cVar, "cVar",,, ) )
ATail(GetList):Display()

Está bien claro que es mucho mas simple, fácil de interpretar y hasta funcional usar la primera opción en este caso.

Hecha esta pequeña entrada vamos al plato fuerte del día, el comando @...Get()
Con el cuchillo bien afilado y el tenedor en la otra mano vamos a proceder a desmembrar este comando para poder digerirlo mejor.
Volvamos al ejemplo número dos:

@ 10,10 Get cVar

Es equivalente a

SetPos( 10, 10 )
AAdd( GetList, _GET_( cVar, "cVar",,, ) )
ATail(GetList):Display()

Pero ¿que hacen estas funciones por separado?
Ahí viene la respuesta, SetPos() como vimos anteriormente coloca la posición del cursor en el lugar correcto, los programadores de Clipper usaron acertadamente SetPos() en lugar de DevPos() ya que un comando "Get" no se puede hacer en la impresora ó en un archivo, es una comando exclusivo del controlador primario de salida (la pantalla).

La función AAdd() como ya la conocerás, de otra forma este tutorial no es para ti, ya que requiere de muchos conocimientos previos, agrega un nuevo elemento al final de un array.
A esta altura te estarás preguntando que elemento es el que hay que agregar en que array, la respuesta es simple, el objeto "Get".
Así es, Get no es mas que un objeto, así como lo es "TBrowse" y otros que maneja Clipper.
La función Atail() que devuelve el valor mas alto de un array y la indocumentada función Display() en este caso no son demasiado funcionales, el compilador Clipper las agrega por desconocer el tipo de variable en tiempos de compilación.

Este último punto es el motivo de por lo menos el 30% de las redundancias en las funciones internas de Clipper, el compilador al igual que todos los que conozco no analiza en tiempos de compilación el tipo y largo de las variables, no los culpo, ya que hacerlo sería equivalente a hacer un compilador con AI(Inteligencia artificial), como a la practica esto es casi imposible los desarrolladores debieron buscar métodos mas abordables para que los distintos comandos y funciones sean perfectamente ejecutados y cumplan con lo esperado.

En pocas palabras, a lo largo de este tutorial encontraremos muchos comandos que llaman funciones redundantes o que tienen "código muerto" como en este caso la función Atail() que en este caso devuelve un valor que no es utilizado en absoluto.

Volviendo al "objeto Get" a partir de ahora así llamaremos al comando conocido como "@...Get()"
Como es de saber el objeto "Get" soporta varios comandos que funcionan como parámetros (palabras clave) que modifican su uso, en este texto solo me voy a referir a unos pocos que a mi parecer no se encuentran bien detallados o su explicación es ambigua, ya que el resto de los parámetros se encuentran claramente detallados en los manuales originales, guías como ser las "Norton Guides" y otros tantos textos que se pueden bajar libremente de Internet.

Comenzaremos por el parámetro "Range", este parámetro admite dos valores, un máximo y uno mínimo, según lo que dice el manual y muchas otras guías como las "Ng's" este comando impide que se entre un valor fuera del rango establecido, pero hoy les cuento que no es así.
Veamos el siguiente ejemplo:

Scroll()
nVar := 10000
@ 10, 10 Get nVar range 10, 30
Read
Scroll()
QOut(nVar)

Si lo compilamos notaremos que no nos permite ingresar ningún valor menor a diez ó mayor a 30, pero si lo ejecutamos nuevamente y presionamos <Enter> notaremos que el valor de la variable es 10000, de la misma manera que si fuera 0, por lo que podemos deducir que el parámetro "Range" solo compara valores directamente ingresados y no el valor original de la variable.
Tal vez esto no sea un "bug", pero los manuales son muy ambiguos al explicarlo, ninguno menciona que la variable de entrada debe tener un valor dentro del rango ó mejor dicho, que si se presiona <Enter> sin modificar el valor el parámetro "Range" fallará.

Para evitar este error es conveniente usar el parámetro "Valid" llamando a una función nuestra que valide los ingresos.

El parámetro When también posee una explicación no muy clara, al punto que he recibido varias consultas de programadores preguntándome como hacer que se ejecute algún código en el momento que el usuario se posiciona sobre un objeto "Get" determinado.
El manual debería resumir:
"When" y "Valid" ejecutan bloques de código de la misma manera, trabajan exactamente igual, la diferencia entre uno y otro es que "When" ejecuta el bloque de código o función determinada en el momento en que el usuario de nuestro programa se posiciona sobre el objeto "Get" y "Valid" en el momento en que el usuario abandona el objeto.
Es una verdadera lástima que los programadores que diseñaron Clipper no hayan pensado en un parámetro que se ejecute con cada tecla presionada como así tampoco en un parámetro que habilite caracteres de mascara para enmascarar una contraseña "Password".

Otro parámetro muy útil es "Pict" o "Picture" como aparece en los manuales, a partir de este momento solo nombraré los comandos y parámetros con las cuatro primeras letras de los mismos tal como las interpreta el compilador Clipper.

El parámetro "Pict" admite variables, esto es muy útil, observemos el siguiente código utilizado en una función de un programa de facturación el cual debe admitir decimales en la cantidad solo cuando el producto se venda por peso, para eso tenemos en la base de artículos un campo lógico llamado "xpeso" el cual indica la condición.

[...]
nDec := Iif(xPeso, 3, 0)
@... Get nBultos Pict [9, nDec]
[...]

De una manera muy compacta logramos en solo un comando cumplir con el requisito dando así un aspecto mucho mas limpio a nuestro código y haciéndolo mucho mas simple de comprender para otros programadores.

Un terrible "Bug", a partir de ahora así llamaré a los errores en programas, es el parámetro "Mess", este de una manera muy distinta a lo que marcan los manuales no funciona con "GET", en su lugar hay que llamar a una rutina usando el parámetro "When".

De todas maneras, quiero dejar bien en claro que no estoy desmereciendo el trabajo de nadie, yo como miles de programadores Clipper en todo el mundo me he valido mucho de los manuales y ni hablar de las "NG's" (Norton Guides), las cuales aún uso cuando no recuerdo algún comando, función o método.

Este tutorial tiene el mismo objetivo que esta página, colaborar con la comunidad de programadores Clipper.

Esta sección sobre CA-Clipper está coordinada íntegramente por Diego Lucio D'Onofrio


la Luna del Guille o... el Guille que está en la Luna... tanto monta...

Estadísticas desde el 01/Nov/2002 23:15