(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.
|