Serie Ficheros Virtuales

 




JavaScript Ficheros Virtuales

 





Conversión de C a JavaScript por procedimientos


 1 Introducción

 2 La conversión de variables en la rutina de búsqueda dicotómica. Parámetros de entrada. Valores de retorno

 3 El paso de parámetros de entrada/salida. La función W_INF

 
 4 Series de doble entrada

 
 5 Página de Muestra



                                                                                                 
_______



1 Introducción

JavaScript es un lenguaje versátil y débilmente tipado. Permite desarrollos procedimentales clásicos y desarrollos utilizando clases bajo programación orientada a objetos. El núcleo de su sintaxis se comparte con el de C++ y Java, pero añade su propia filosofía que origina diferencias tanto más importantes cuanto más sutiles son.

Podemos fijarnos para concretar en la definición de variables. Hay un aspecto evidente como la propia sintaxis declarativa, pero la sutileza aparece en cuanto al ámbito, y es vital. Así, las variables globales no lo son en cuanto al módulo sino en cuanto a la hoja en uso (Y en los marcos, se extiende a las hojas hijas).

Esto es muy importante, porque aunque podemos transcribir desarrollos procedimentales de C a JavaScript y obtener desarrollos plenamente funcionales tal y como se va a mostrar en la primera parte del blog, estos desarrollos resultan vulnerables y precisan mecanismos de encapsulación que sustituyan la protección automática que el ámbito global por módulo proporcionaba. Este será el objeto de la segunda parte del blog.

No es la única sutileza que conlleva la versatilidad de JavaScript, pero de entrada es la más importante porque obliga a cambiar la forma de ver las cosas. Sin embargo, creo mucho más preferible que un lenguaje peque por ser versátil que por ser restrictivo, porque las oportunidades vuelven a un lenguaje interesante y atractivo.

El capítulo repasará las adaptaciones que la reescritura de los procedimientos originales escritos de soporte de los ficheros virtuales escritos en C ANSI han necesitado para su transcripción directa a JavaScript y que he denominado JavaScript por procedimientos.

La edición del código la he realizado en el propio editor de Visual Studio que utilicé para escribir los proyectos anteriores, puesto que ya lo conozco de antes y reconoce los fuentes .js aunque no se lleguen a compilar como proyecto. La depuración la sigo con el FireBug que viene de serie en las herramientas del navegador de FireFox. También he probado las de otros navegadores y hoy por hoy este es el que más me gusta (En realidad me gustaría disponer de un todo en uno, pero siempre es necesario elegir). En algún momento utilizo otros editores que he encontrado en la red como Aptana y MAX's HTML Beauty++ 2004, el primero porque presenta un árbol de proyecto más conseguido que el de Visual Studio, mientras que el segundo lo utilizo para centrar números de líneas especificas tras la depuración con FireBug. Últimamente también utilizo NetBeans, NanoWebEditor y Adobe Go Live, estos dos últimos en una versión suministrada por mi proveedor de internet, pero NetBeans es un IDE gratuito muy completo y de propósito general.

                                                                                                 
_______



2 La conversión de variables en la rutina de búsqueda dicotómica. Parámetros de entrada. Valores de retorno

La adaptación de las variables comienza con la rutina de búsqueda dicotómica que constituye el núcleo de la metodología.

Es una rutina sencilla que ilustra la recepción de parámetros de entrada, tanto en versión individual como en series, la definición de variables de trabajo y el uso de valores de retorno múltiples.

La versión original de la rutina se encuentra en el programa de servicio para utilidades SRRCU, con el nombre SRRCU_BS y se ha transcrito como U_BS.

A continuación se presentan ambas versiones en forma de tabla de doble entrada para facilitar la comparación.

                              SRRCU_BS.cpp

                                    U_BS.js

//------------------------------------------------------------------- 

//--------------------------------------------------------------

// Función SRRCU_BS

// Función U_BS

//

//

// Descripción: Localiza en una serie ascendente un ítem

// Descripción: Localiza en una serie ascendente un ítem

//              La serie se enlaza según una serie índice asociada

//              La serie se enlaza según una serie asociada

//

//

// Parámetros:

// Parámetros:

// (I)         d: Dato a localizar

// (I)         d: Dato a localizar

//         iSize: Tamaño del dato

//         lSize: Tamaño del dato

//           nSd: Dimensión efectiva de la serie de datos e índices

//           nSd: Dimensión efectiva de la serie de datos e índices

//            Si: Puntero a serie índice

//            Si: Serie índice

//            Sd: Puntero a serie de datos

//            Sd: Serie de datos

// (O)     iComp: Variable auxiliar resultado memcmp: - 0 +, < = >

//

//

//

// Retorno:

// Retorno: Devuelve n & iComp en una serie de dos elementos

//             n: Indice de posición encontrado

//  

//                Si iComp = 0, la localización es exacta

//  Elemento 0

// n se devuelve en formato 1..nSd, teniendo en cuenta que

//       n: Indice de posición encontrado

// Si la localización es inexacta, se devuelve el índice más próximo

//          Si iComp = 0, la localización es exacta

// inferior o superior según el valor de iComp

// n se devuelve en formato 1..nSd, teniendo en cuenta que

// Si d está fuera de rango inferiormente, se devuelve 0

// Si la localiación es inexacta, se devuelve el índice más próximo

//

// inferior o superior según el valor de iComp

//

// Si d está fuera de rango inferiormente, se devuelve 0

//

//

//

// Elemento 1

//

//   iComp: Variable auxiliar resultado comparación: - 0 +, < = >

//

//

//-------------------------------------------------------------------

//-----------------------------------------------------------------

long SRRCU_BS(const void *d,  int iSize,      long nSd,

function U_BS(d, lSize, nSd, Si, Sd)

              const long *Si, const void *Sd, int *iComp)

 

{

{

 long a=0,b=0,ab2=0;// Indices de rango en el algoritmo de búsqueda

 var a, b, ab2;    // Indices de rango en el algoritmo de búsqueda

 char *cSd;         // Uso de Sd como char* (Para facilitar debug)

 var wd, wSd;      // Uso de substring de d y Sd

 char *cd;          // Uso de d  como char*

 

 

 var Rt = new Array(2); // Serie RETORNO

 

 

 // Inz

 // Inz

 cSd = (char *) Sd;

 Rt[0] = 0;

 cd  = (char *) d;

 Rt[1] = 0;

 

 

 

 

 

 // Paso de variable de trabajo

 

 

 

 wd = d.slice(0, lSize); // Porción significativa a comparar

 

 

 

 

 // Rango inicial

 // Rango inicial

 

 

 a = 0;

 a = 0;

 b = nSd;

 b = nSd;

 

 

 

 

 // Valor extremo inferior s/índice

 // Valor extremo inferior s/índice

 

 

 *iComp = memcmp(cd, cSd + Si[a]*iSize, iSize);

 wSd = Sd[Si[a]].slice(0, lSize);

 

 

 

 

 

 // Valor fuera de rango inferior

 

 

 if (*iComp<0) return a;

 if (wd < wSd)

 

 {

 

   Rt[0] = a;

 

   Rt[1] = -1;

 

   return Rt;

 

 }

 

 

 

 

 

 // Valor en el extremo inferior

 

 

 if (!*iCompreturn 1;

 if (wd == wSd)

 

 {

 

  Rt[0] = 1;

 

  Rt[1] = 0;

 

  return Rt;

 

 }

 

 

 

 

 // Valor extremo superior

 // Valor extremo superior

 

 

 *iComp = memcmp(cd, cSd + (Si[b-1] * iSize), iSize);

 wSd = Sd[Si[b-1]].slice(0, lSize);

 

 

 

 

 

 // Valor fuera de rango superior

 

 

 if (*iComp>=0) return  b;

 if (wd > wSd)

 

 {

 

  Rt[0] = b;

 

  Rt[1] = 1;

 

  return Rt;

 

 }

 

 

 

 

 

 // Valor en el extremo superior

 

 

 

 if (wd == wSd)

 

 {

 

   Rt[0] = b;

 

   Rt[1] = 0;

 

   return Rt;

 

 }

 

 

 

 

 // Ciclo de Contracción Dicotómica

 // Ciclo de Contracción Dicotómica

 

 

 do

 do

 {

 {

  ab2 = (a + b) >> 1;  // (a+b)/2

  ab2 = (a + b) >> 1;  // (a+b)/2

 

 

 

 

  // Comparación en curso

  // Valor en curso

 

 

  *iComp = memcmp(cd, cSd + (Si[ab2-1] * iSize), iSize);

  wSd = Sd[Si[ab2-1]].slice(0, lSize);

 

 

 

 

  // Localización exacta

  // Localización exacta

 

 

  if (!*iComp) return ab2;

  if (wd == wSd)

 

  {

 

   Rt[0] = ab2;

 

   Rt[1] = 0;

 

   return Rt;

 

  }

 

 

 

 

 

  // Marca de comparación

 

 

 

  Rt[1] = -1;

 

  if (wd > wSd) Rt[1] = 1;

 

 

 

 

  // Localización inexacta

  // Localización inexacta

 

 

  if (ab2 <= a) return a;

  if (ab2 <= a)

 

  {

 

   Rt[0] = a;

 

   return Rt;

 

  }

 

 

 

 

  // Compara, Divide Rango y Continua Ciclo de Proceso

  // Compara, Divide Rango y Continua Ciclo de Proceso

 

 

  if (*iComp > 0) a = ab2; else b = ab2;

  if (Rt[1] > 0) a = ab2; else b = ab2;

 

 

 } while (a < b);

  } while (a < b);

 

 

 

 

 // Retorna índice asociado

 // Retorna índice asociado

 

 

 return ab2;

 Rt[0] = ab2;

 

 return Rt;

}

}


                                                                                                 
_______

Discusión


  JavaScript no precisa especificar el tipo de las variables. El lenguaje está débilmente tipado y el tipo efectivo se determina durante la ejecución de forma contextual.



  Por ello, en la definición de parámetros nos encontramos que una sintaxis tan detallada y prolija en C como

 
      long SRRCU_BS(const void *d, int iSize, long nSd, const long *Si, const void *Sd, int *iComp

  se transcriba simplemente como

       function U_BS(d, lSize, nSd, Si, Sd)

  en JavaScript.


  Durante la ejecución, el contexto deberá proporcionar a cada uno de los parámetros indicados el tipo adecuado para que el resultado sea satisfactorio.



  Adicionalmente, la función U_BS retornará un vector que integra el valor del índice resultado (el n de tipo long que devolvía la serie original), junto con el resultado de la comparación de detalle (el iComp de tipo int que se pasaba por referencia [Esto es, como *iComp] para hacerlo de entrada/salida en la función original).

 Se trata de una serie de uso multitipo pero se define simplemente como   var Rt = new Array(2); 

 Este es uno de los formatos de definición de series de los que posteriormente veremos otras alternativas.

 El uso del retorno es una alternativa al paso de referencia en JavaScript que veremos en el epígrafe siguiente.

 

  La definición de variables locales utiliza la declarativa var sin especificar tipo como en

      var a, b, ab2;

  frente a definiciones tipadas como

      long a, b, ab2;


 
  En el contexto, al ejecutar un instrucción como

          
a = 0;

 
se expresa que    a    pasa a ser una variable numérica.



 
La rutina mantiene la misma estructura lógica, pero sustituye el uso de memcmp por comparativas directas utilizando los operadores
  "  <    >    <=    >=   ==  "

ni que decir tiene que es una verdadera pena que no exista un memcmp en todos los lenguajes y situaciones, por muy heterodoxo que pudiera resultar.

                                                                                                 
_______



3 El paso de parámetros de entrada/salida. La función W_INF

El prototipo original de SRRCW_INF es

//-------------------------------------------------------------------------------------
// FUNCION....: SRRCW_INF                                           
//                                                                  
// DESCRIPCION: Recupera información estadística de un fichero      
//                                                                  
// PARAMETROS.:                                                     
// (entrada) cFILE: Nombre de fichero solicitado                    
// (salida ) iDIMC: Tamaño clave                                    
//           lDimD: Tamaño datos
//          lNITEM: Nºde items archivados (, de los que)
//          lBAJAS: (es el)Nºde items que son baja (y )
//          lNIDD : (es el)Nºde items que aún pueden agregarse
//                                                                
// RETORNO....:                                                     
//                 0: Información recuperada satisfactoriamente     
//                 1: Error de Proceso (No encontrado(EOF), ...)    
//       
//-------------------------------------------------------------------------------------
short int SRRCW_INF(const char *cFILE, int *iDimCp, long *lDimDp, long *lNITEMp, long *lBAJASp, long *lNIDDp)



El prototipo de la función convertida W_INF es

//-------------------------------------------------------------------------------------
// FUNCION....: W_INF                                           
//                                                                  
// DESCRIPCION: Recupera informacion estadistica de un fichero      
//                                                                  
// PARAMETROS.:                                                     
// (entrada) cFILE: Nombre de fichero solicitado                    
// (salida ) iDIMC: Longitud clave                                    
//           lDimD: Longitud datos
//          lNITEM: Num.de items archivados (, de los que)
//          lBAJAS: (es el)Num.de items que son baja (y )
//                                                                
// RETORNO....:                                                     
//                 0: Informacion recuperada satisfactoriamente     
//                 1: Error de Proceso (No encontrado(EOF), ...)    
//       
//-------------------------------------------------------------------------------------
function W_INF(cFILE, iDimCp, lDimDp, lNITEMp, lBAJASp)

En esta función todos los parámetros de salida se pasan por referencia, lo que en JavaScript significa que se pasan como series, pero no se indica de forma explícita. En C sin embargo el paso por referencia si se expresa explícitamente con la sintáxis *iDimCp.

Prescindiendo de la sintaxis, la idea es la misma en ambos lenguajes, pues cuando pasamos en C una dirección tambien pasamos implícitamente la posible serie que comienza en esa dirección
.


Es muy importante tener en cuenta que la resolución contextual que esta sintaxis conlleva es la fuente del principal trabajo de depuración que conlleva la traducción de las rutinas.

Veámoslo con un fragmento de código ejemplo del uso de W_INF. (Aquí introducimos una alternativa de la sintaxis de definición de series que vimos en el epígrafe anterior)


 // Variables para W_INF
 
 var iDimC  = [0]; // Serie para paso de longitud de clave del fichero solicitado (Inicializando el 1er. elemento como valor numerico nulo)
 var lDimD  = [0]; // Serie para paso de longitud de datos del fichero solicitado
 var lNITEM = [0]; // Serie para paso del nº de ítems en el fichero,
 var lBAJAS = [0]; // de los que lBAJAS son baja
 
 
 // Recupera estadísticos en curso del fichero de pruebas y toma el num.de items depurado de bajas
 
 var er = W_INF("PRUEBASW", iDimC, lDimD, lNITEM, lBAJAS);

 var NITEM = lNITEM[0] - lBAJAS[0];
 

 Al transcribir, es muy fácil escribir     
NITEM = lNITEM - lBAJAS;   lo que origina un error de ejecución en el mejor de los casos, o es la fuente de un bug latente en el peor.

                                                                                                 
_______


4 Series de doble entrada

En SRRCM se definen las series dobles de soporte del sistema de ficheros virtuales. Concretamente su definición es

static
long **lINDICES;   // Conjuntos de índices
static char **cCLAVES;    // Conjuntos de claves.                          
static char **cDATOS;     // Conjuntos de datos asociados.                  


Asociadas a ellas se tienen las series de control principal siguientes


static int *iSDimC;       // Dimensiones de claves por cada grupo           
static long *lSDimD;      // Dimensiones de datos  por cada grupo           
static long *lNITEM;      // Contadores de items por grupo  
static long *lBAJAS;      // Id.de bajas pdts.de depurar de memoria * grupo 
static long *lERASES;     // Id.de grupos eraseados para reutilizar
static short int *iSTAT;  // 0/1 Grupo Activo: 1 en NEW, 0 en DEL           



La transcripción de la definición en W.js es

var lINDICES = []; // Conjuntos de indices. (Se declara como una serie genérica sin inicializar. Luego se establecerán sus elementos a su vez como series)
var cCLAVES = [];  // Conjuntos de claves.                          
var cDATOS = [];   // Conjuntos de datos asociados.                  

var iSDimC = [];   // Dimensiones de claves por cada grupo           
var lSDimD = [];   // Dimensiones de datos  por cada grupo           
var lNITEM = [];   // Contadores de items por grupo  
var lBAJAS = [];   // Id.de bajas pdts.de depurar de memoria * grupo 
var lERASES = [];  // Id.de grupos eraseados para reutilizar
var iSTAT = [];    // 0/1 Grupo Activo: 1 en NEW, 0 en DEL       

                                                          
_______
 

En esta definición simplemente se declaran las series que posteriormente el contexto establecerá como simples o dobles y resolverá su tipo. Esto se aprecia en los fragmentos principales de las funciones de creacción de ficheros virtuales M_NEW y en la función de escritura de registros en un fichero virtual M_WRITE que se presentan a continuación

//-----------------------------------------------------------------
// FUNCION....: M_NEW                                             
//                                                                
// DESCRIPCION: Genera un identificador NID para un nuevo fichero virtual
//                                                                
// PARAMETROS.:                                                   
// (entrada) iDimC: Dimension claves del nuevo grupo de Items     
//           lDimD: Dimension datos  del nuevo grupo de Items     
//                                                                
// RETORNO....:                                                   
//                 0: Error de Proceso (Memoria agotada, ...)     
//                >0: Nuevo Identificador (NID)                   
//                                                                
//-----------------------------------------------------------------
function M_NEW(iDimC, lDimD)
{
 . . . (Aqúi estaría el progreso/reutilización del NID en curso)


 // Reserva Dimensiones del NID solicitado (En formato 0...N-1)

 iSDimC[lNIDw] = iDimC;  // Asigna valor numérico a series de acceso con índice único
 lSDimD[lNIDw] = lDimD;
 iSTAT[lNIDw]  = 1;
 lNITEM[lNIDw] = 0;
 lBAJAS[lNIDw] = 0;
 cDATOS[lNIDw] = [];     // Indica que las series de datos son de doble índice, que se irán rellenando con la función M_WRITE que sigue a continuación
 cCLAVES[lNIDw] = [];
 lINDICES[lNIDw] = []; 
 
 . . .


 // Devuelve el NID asignado en formato 1..N

 return ++lNIDw;
}                                

                                                                                                 
_______


/*-----------------------------------------------------------------*/
/* FUNCION....: M_WRITE                                            */
/*                                                                 */
/* DESCRIPCION: Salva un Item a memoria                            */
/*                                                                 */
/* PARAMETROS.:                                                    */
/* (entrada) lNIDp: Identificador del Conjunto de Items asociado   */
/*           vClav: Clave del Item                                 */
/*           vDato: Datos del Item                                 */
/*                                                                 */
/* RETORNO....:                                                    */
/*              0: Error de Proceso (Memoria out, ya existe...)    */
/*             >0: Indice de Item-Clave Guardado Satisfactoriamente*/
/*                    (Formato 1..N)                               */
/*-----------------------------------------------------------------*/
function M_WRITE(lNIDp, vClav, vDato)

 . . .
 

 // Incrementa contador de Items en el Grupo
 
 lNIDc = lNIDp - 1;
 ++lNITEM[lNIDc];                  // lNITEM tiene uso numérico con índice único
 
 lNITEM1 = lNITEM[lNIDc]-1;
 
 
 
 // Paso a Memoria del Item dado


 // -Parte de Datos
 
 cDATOS[lNIDc][lNITEM1] = vDato;   // cDATOS utiliza un doble índice y archiva la parte de datos del registro del fichero virtual en curso


 // -Parte de clave

 cCLAVES[lNIDc][lNITEM1] = vClav;
 

 . . .

                                                                                                 
_______

 


5 Página de muestra

 

Este desarrollo inicial de ficheros virtuales en javascript por procedimientos se presenta en una página de muestra de pruebas, que sigue el estilo de la aplicación de escritorio que se utilizaba en las pruebas de C Ficheros Virtuales, en donde una relación de botones permite ir probando los módulos principales de forma individual o conjunta, para los tamaños de filas y columnas que se indiquen.

 

 

                                                                                                  _______