LOS CODEBLOCKS. (Humberto Rosario Díaz)


Los Codeblocks (CBs en adelante) son tipos de datos que se pueden utilizar para aumentar el rendimiento de un programa y reducir la cantidad de código que es necesario escribir. Los CBs pueden proporcionar la misma funcionalidad que los macros, pero; son mas flexibles y eficientes.

Clipper utiliza los CBs internamente para inplementar los mandatos estándar. Utiliza CBs para implementar ámbitos "While", condiciones "For", expresiones de indice "SET KEY", y mucho más. Los CBs son el equivalente actual del operador de macro. El operador de macro funciona bien en un ambiente interpretado, pero resulta engorroso de manejar en un lenguaje como Clipper.

Para aquellos que saben "C" los CBs de Clipper les recordarán a los punteros y las funciones de C. Estos punteros a funciones en C son tipos de datos, y contienen exactamente lo que su nombre inplica, es decir, un puntero a una función. Esto es similar a la función Eval() de Clipper.

Los CBs son nuevos tipos de datos. Se almacenan en variables, al igual que otros tipos de datos que nos son familiares, tales como los númericos y de fecha. Se pueden pasar CBs como parámetros, devolverlos desde funciones y copiarlos en otras variables. No obstante el tipo de datos que contienen es algo diferente al otro tipo de datos de Clipper. Hasta ahora ya hemos utilizado variables para almacenar datos tales como nombres y direcciones, números de telefonos y estados de cuentas. Sin embargo los codeblocks almacenan parte de código de programa. Se pueden pasar esas partes de código a subrutinas (funciones y procedimientos) y luego ejecutar esa parte de código.

Creamos un CBs utilizando una sintaxis bastante grotesca:

BVar : { | | Test() }

El carácter { significa "comienzo del codeblock" y el } "final del codeblock". Los dos caracteres | | delimitan la lista de parámetros formales del Codeblock. La variable bVar puede ser local, static, private o public, no hay diferencia. Solo contiene una parte de código, de manera que cuando se evalúe llame a la función Test. Para evaluar o ejecutar un codeblock ejecutamos la función "Eval()" como en:

NResult := Eval( bVar )

Esto llama a la función Test. Como todas las funciones, Eval() devuelve un resultado: el resultado de la expresión contenida en el Codeblock. Y esto es casi todo lo que se necesita saber sobre los Codeblocks.

Libro: Clipper 5.2 Guia Avanzada para el Programador. – Rick Spence –

Ed. Addison Wesley Iberoamericana. Libros ra-ma.

 

 

LOS CODEBLOCKS. (René Flores)


El tipo de dato codeblock es una maravilla implementada en Clipper 5.01 y superiores.

Lo fantastico de este tipo de dato que se pueden hacer verdaderas maravillas como por ejemplo un interprete de Clipper hecho en Clipper, un interprete de formulas en tiempo de ejecucion, e incluso se pueden escribir expresiones externas a un programa e interpretarlas en tiempo de ejecucion del mismo, pasarlos como parametros, etc.

El verdadero poder de Clipper se obtiene cuando dominas los bloques de codigo, a tal grado que dices .... ¿ a poco se puede hacer esto con Clipper ?

Bajo C, los bloques de codigo pueden compararse mas que a apuntadores, a lo que se llama macros.

Una de las razones por la que personalmente no he abandonado Clipper es precisamente los bloques de codigo, cosa que no he encontrado en ningun otro lenguaje mas que en Visual dBase, pero el overhead del producto es muy grande.

Creo haber visto por ahi (creo que en mi pagina de enlaces esta) una direccion donde hay un producto llamado Clipper Functions for Delphi, que creo que ofrece soporte a bloques de codigo para este.

Saludos

Rene Flores

LOS CODEBLOCKS II. (René Flores)


Hola listeros:

Como lo prometido es deuda y el que paga descansa, en esta ocasión continuaremos con nuestra leccion sobre bloques de codigo y su aplicacion sobre arreglos.

El duo dinamico: Bloques de codigo y arreglos.

Una de las grandes ventajas de Clipper 5.x es su excelente y sublime sistema de arreglos de multiples dimensiones y la maravilla de poder crearlos en tiempo de ejecucion del programa. En realidad los arreglos dinamicos en Clipper son apuntadores a direcciones de memoria, los que han trabajado con C y con Pascal seguramente prefieren no acordarse de lo que son los apuntadores a memoria porque trabajar con ellos es similar a ser un domador de leones, es un trabajo de alto riesgo y ademas no sabes en que momento va a saltar la fiera, lo menciono porque si has trabajado con apuntadores en C o Pascal, seguramente mas de una vez has destruido el sistema operativo de tu equipo, suele pasar, en fin, deciamos pues que en Clipper los arreglos son en realidad apuntadores a memoria, no te preocupes sobre lo que comente sobre los apuntadores de C o Pascal, los de Clipper estan perfectamente bien controlados. :-)

Los bloques de codigo y los arreglos en combinacion forman la pareja mas dinamica de la programacion en Clipper ya que un bloque de codigo te puede ahorrar muchisimas lineas de programacion y de operaciones sobre arreglos ademas de ser muchisimo mas rapido que el tipico FOR x := 1 to LEN(arreglo).

En la oportunidad anterior hablamos sobre la funcion EVAL() y comentamos que dicha funcion servia para evaluar un bloque de codigo, ahora bien para utilizar arreglos con bloques de codigo utilizaremos la funcion:

AEVAL(<aArreglo>, <bBloque>, [<nInicio>], [<nCuantos>]) --> aArreglo

Donde

<Arreglo> es el arreglo que queremos procesar <bBloque> es el bloque de codigo que queremos aplicar A CADA ELEMENTO del arreglo <nInicio> es el elemento del arreglo sobre el cual queremos comenzar a ejecutar el bloque de codigo, si se omite, el valor por omision es 1 <nCuantos> es el numero de elementos contados a partir de la posicion <nInicio> sobre los cuales queremos ejecutar el bloque de codigo, si este se omite, el valor por omision es precisamente la longitud del arreglo.

A diferencia del EVAL que retorna el ultimo valor proveniente de la evaluacion del bloque de codigo, AEVAL retorna el mismo arreglo sobre el cual trabajamos, esto nos permite modificar el arreglo en tiempo de ejecucion.

Veamos algunos ejemplos:

Supongamos que quiero sumar el contenido de los siguientes arreglos:

a := {2,4,6,8} b:={1,3,5,7,9}

Segun el metodo tradicional, tendriamos que hacer algo como esto

x := 0 y:= 0

FOR j := 1 to LEN(a) x += a[j] NEXT

FOR j := 1 to LEN(b) y += b[j] NEXT

Con bloques de codigo solo tendriamos que hacer esto:

x := 0 y:= 0

AEVAL(a,{| j | x += j}) AEVAL(b,{| j | y +=j})

¡ Exactamente la mitad del codigo original !, si en el ejercicio tradicional utilizamos 8 lineas de codigo, en el ejercicio con bloque utilizamos unicamente 4.

Nota por favor que cuando trabajamos con arreglos y bloques de codigo, a este ultimo se le pasan de manera automatica dos parametros (| j |, se pudo haber llamado de cualquier forma), que indica el elemento del arreglo en el cual estoy posicionado actualmente y un segundo parametro, el cual veremos mas adelante que indica la posicion dentro del arreglo.

Continuando con el ejercicio anterior, supongamos que queremos sumarle 2 unidades a cada elemento del arreglo "b", segun lo explicado anteriormente, quiza pudieras pensar que la solucion este ejercicio es la siguiente:

AEVAL(b,{| j | j := j + 2})

Esta solucion parece logica si piensas: el parametro "j" toma el valor del elemento actual del arreglo, por lo tanto, si le sumo dos a este parametro, queda solucionado el ejercicio...... BIIIIIIIIPPPPP, respuesta equivocada.

El parametro "j" toma temporalmente el valor del arreglo en la posicion en la cual me encuentro actualmente, en la primera vuelta, "j" vale 1 (primer elemento del arreglo), si le sumo un 2, "j" ahora vale 3, pero quien vale 3 es "j" y no b[1], que es en realidad quien queremos que valga 3, en la siguiente vuelta de la evaluacion del bloque de codigo "j" vale 3 (segundo elmento del arreglo), si le sumo 2, "j" vale 5 y volvemos a tener el mismo problema.

La solucion pues a este ejercicio, consiste en utilizar el segundo Parametro que se le pasa de manera automatica al bloque de codigo, este Parametro es la posicion dentro del arreglo en la cual me encuento posicionado, quedando la solucion de esta manera: AEVAL(b,{|j,k| b[k] := j + 2})

En la primera vuelta de la evaluacion, "j" vale 1 (primer elemento) y "k" tambien vale 1 (primera posicion) luego entonces al asignar b[k] := j + 2, en realidad estoy haciendo b[1] := 1 + 3, en la segunda vuelta "j" vale 3 (segundo elemento) y "k" vale 2 (segunda posicion), entonces la operacion queda b[2] := 3 + 2, en la tercera vuelta "j" vale 5 (tercer elemento) y "k" vale 3 (tercera poscion) la operacion queda b[3] := 5 + 2 y asi suceviamente hasta que cubrimos todo el arreglo.

De manera similar a como lo hacemos con los bloques de codigo tradicionales, tu puedes definir cuantas operaciones quieras hacer dentro del bloque de codigo siempre y cuando las separes por comas.

Arreglos de dos o mas dimensiones:

Bien, ya hemos visto como aplicar bloques de codigo a arreglos de una dimensio, lo cual fue muy sencillo, ahora veamos como hacerlo en arreglos de dos dimensiones.

El ejemplo clasico de Clipper para la funcion Directory:

aDir := Directory("C:\*.*","D")

Esta funcion llena un arreglo de dos dimensiones donde cada elemento del arreglo a su vez es otro arreglo que contiene informacion sobre cada archivo del directorio que hemos solicitado.

Supongamos que queremos desplegar en pantalla los nombres de los archivos (primer elemento de cada arreglo que compone aDir). Con el metodo tradicional:

FOR j := 1 TO LEN(aDir)

QOUT(aDir[j,1])

NEXT

Con bloques de codigo:

AEVAL(aDir,{|aArr| QOUT(aArr[1]})

De 3 lineas de codigo nos vamos a una sola, expliquemos este ejemplo:

Supongamos que el arreglo aDir tiene una estructura similar a la siguiente:

aDir[1] := {"command.com", 38678 , 24/12/99, "01:40:00", "F"} aDir[2] := {"leeme.txt", 8543 , 11/12/99, "23:00:00", "F"} aDir[3] := {"windows", 0 , 24/12/99, "01:40:00", "D"} aDir[4] := {"archivos", 0 , 24/12/99, "01:40:00", "D"}

Al aplicar al arreglo aDir nuestro bloque de codigo:

AEVAL(aDir,{|aArr| QOUT(aArr[1]})

En la primera vuelta de la evuacion, el parametro aArr toma como valor el primer elemento de aDir, en este caso, este valor es el arreglo {"command.com", 38678 , 24/12/99, "01:40:00", "F"}, de este arreglo queremos desplegar unicamente la primera posicion, asi que hacemos el QOUT(aArr[1]) y se despliega en pantalla "command.com", en la segunda vuelta de la evaluacion, aArr se carga con {"leeme.txt", 8543 , 11/12/99, "23:00:00", "F"} y al momento de hacer el QOUT(aArr[1]) se despliega en pantalla "leeme.txt", se continua de esta manera hasta que se evalua todo el arreglo aDir.

Quiza te puedas preguntar.... ¿ puedo anidar bloques de codigo para arreglos ?, claro que si, lo unico que debes tomar en cuenta es que no debes repetir las variables que recibes como parametros. Veamos este ejemplo:

Supongamos que queremos sumar el tamaño de todos los archivos en nuestro Arreglo aDir

nTotbytes := 0 AEVAL(aDir, {| aArr | AEVAL(aArr, {|i,j| nTotBytes := IIF(j = 2, i,0)})})

Este ejemplo pudo hacerse mas facilimente y sin AEVALS anidados pero lo hice asi por razones didacticas, vemos que hice:

En primera vuelta de la evaluacion, el primer AEVAL, el que trabaja sobrel aDir, le envia como parametro a aArr, su primer elemento, en este caso: {"command.com", 38678 , 24/12/99, "01:40:00", "F"}, que es el valor que toma aArr, como aArr ahora es un arreglo, puede ser perfectamente evaluado dentro de otro AEVAL, que es el segundo, dentro de este, el parametro "i" vale "command.com", el parametro "j" un 1 en la primera evaluacion, en la segunda evaluacion "i" valdra 38678 y "j" un 2 (recuerda, elemento - posicion), y es aqui donde la funcion IIF() decide si se suma este valor, en la tercera vuelta "i" vale 24/12/99 y "j" vale 3 y asi sucesivamente hasta que se cubre la totalidad del arreglo.

Un caso mas practico:

Hagamos un pequeño programa que nos ayude en la edicion de todos los datos de un registro:

USE archivo.dbf

aEstruct := DBSTRUCT() // obtenemos la estructura del DBF en uso

/* generamos un arreglo de longitud similar a la estructura */ aDatos := ARRAY(LEN(aEstruct))

/* inicializamos el arreglo aDatos con los valores de todos los campos de la base de datos */ AEVAL(aDatos,{|x,y| aDatos[y] := FieldGet(y)})

/* limpiamos la pantalla */ CLS

/* y ahora editamos los datos, esto lo tendre que hacer de la manera tradicional porque los bloques de codigo no aceptan comandos, pero podria hacerlo con bloques de codigo.... porque no lo intentas tu mismo ? */

nReng := 0 FOR x := 1 LEN (aEstruc) @ nReng, 0 SAY aEstruc[x,1] GET aDatos[x] nReng++ NEXT

READ

/* y ahora hacemos los reemplazos de los datos editados en la base de datos */ AEVAL(aDatos,{|x,y| FieldPut(y,x)})

Este es un ejemplo muy burdo (se puede mejorar sustancialmente), pero es excelente para fines didacticos, si eres observador te daras cuenta que este pequeño programa nos permite editar todos lo campos de una base de datos sin necesidad de conocer su estructura previamente, en el metodo tradicional ubieramos tenido que escribir cuando menos 5 veces mas de codigo para obtener los mismos resultados, en este ejercicio son 12 lineas de codigo efectivas (descontando espacios y comentarios).

Daremos por terminado este articulo aqui, no te alarmes si de entrada no entiendes esto, o no te funciona a la primera, antes de lanzarte al fascinante mundo del duo dinamico, te recomiendo que hagas tus operaciones con arreglos por el metodo tradicional y una vez que funcionen, las pases a bloques de codigo, notaras un aumento sustancial en la velocidad de tus aplicaciones una vez que domines los bloques de codigo.

Aun queda pendiente una entrega mas, en donde hablaremos de cómo aplicar el concepto de bloques de codigo sobre bases de datos, algo mas o menos parecido a lo que acabamos de hacer con arreglos, si bien en estos ejercicios nos estamos ahorrando en FOR x ....., cuando trabajamos los bloques de codigo con bases de datos nos ahorramos los DO WHILE ! EOF().

Saludos.

Rene Flores http://www.ciber-tec.com