viernes, noviembre 17, 2006

Barras de herramientas que se guardan en ficheros INI

Recuperada y explicado en la entrada anterior el funcionamiento de mi implementación de los ficheros de texto tipo INI, ahora voy a comentar lo que motivó esa entrada.

Las aplicaciones típicas de Windows tienen barras de herramientas, barras de menús, y si quieres que tu aplicación sea mínimanente interesante, tienes que implementarlas y permitir su personalización, o al menos guardar entre sesiones las posiciones de las mismas. Personalmente pienso que es una chorrada todo ese tipo de cosas en un programa, pero a veces son necesarias por motivos de espacio u organización.

El .NET Framework 2.0 provee de forma bastante sencilla la facultad de tener elementos acoplables casi de cualquier tipo, y de eso nos vamos a aprovechar. Primero debemos soltar en nuestra ficha un componente llamado ToolStripContainer, y le cambiamos la propiedad Dock al valor Fill, que se puede hacer directamente desde la pestaña que tiene el componente como tareas coumunes. También podemos activar/desactivar en qué lados se va a permitir el acople. El resto es muy sencillo: añadir las barras y los componentes que queramos.

Cuando lancemos nuestra aplicación, los elementos aparecerán allí donde se hayan encajado durante el diseño, y permitirán ser movidos de forma sencilla por todod los laterales de la ficha. Pero cuando cerremos nuestra aplicación los cambios se perderán...

¿Cómo guardar el estado de las posiciones? Una solución sería utilizar el sistema de Config del propio .NET, pero hay que currarse a mano todas las variables, otra utilizar los flujos binarios o xml para guardarla. En este último caso ignoro si se puede salvar toda la estructura de la ficha, pero lo dudo. Además, son elementos secuenciales. En ambos casos no son soluciones generales, sino particulares, con lo que tenemos que repetir en cada aplicación nueva o en cada cambio importante que hagamos.

¿Se puede construir un sistema general, y que encima lo pueda trastear el usuario del programa? La respuesta corta: si, pero con condiciones. La respuesta larga consiste en todo lo que sigue a partir de aquí.

Primero tenemos que echar un vistazo a cómo lo hace el propio Visual Studio, por lo que hay que inspeccionar el interior del método InitializeComponent. El asunto está clarísimo: se crean los distintos elementos, luego se van agregando a la instancia del ToolStripContainer mediante la llamada a Add del array de controles, y posteriormente se colocan en su posición mediante la asignación de la propiedad Location.

Facil, ¿no? Pues no. Fuera de InitializeComponent no funciona. Simplemente coloca las barras de herramientas donde mejor le parece al sistema, ignorando en muchos casos hasta si están unas encima de las otras. Incluso aunque dupliquemos exactamente el mismo bloque de código. Haz lo que digo pero no lo que hago.

La solución más ardua sería, una vez terminado el proyecto, y sin usar el editor visual, cambiar lo que queramos dentro de IntializeComponent, pero entonces nos estaríamos metiendo en camisa de once varas, y no es el tema, así que, como dijo mi tío Saltatrampas, si no puedes contra el enemigo, dale una patada en los bajos.

Una inspección en la ayuda sobre el componente ToolStripContainer nos muestra varias cosas interesantes. La primera de todas consiste en que dicho componente tiene cuatro instancias de ToolStripPanel llamadas TopToolStripPanel, LeftToolStripPanel, RightToolStripPanel y BottomToolStripPanel, que se corresponden a los contenedores de los cuatro laterales de la ficha. La segunda es que cada uno de estos componentes contiene a su vez un array del tipo ToolStripPanelRow llamado Rows y que como su nombre indica es una reperesentación de cuántas tiras hay en cada grupo. Y para finalizar, la propiedad Controls, dependiente de éste último, otro array que contiene, ya por fin, los controles anclados a dicho contenedor.

Vista esta estructura, pensamos que si a la hora de cerrar la ficha podemos generar una representación de la misma, luego a la hora de abrirla seremos capaces de volver a reconstruirla. Dicho y hecho, aquí está nuestra solución.

Guardar el estado es bastante sencillo, para cada uno de los ToolStripContainer recorremos todos los elementos de su propiedad Rows, y dentro de ésta todos los elementos de Controls, guardando finalmente el nombre y el Location de cada uno de ellos:

int iRows,iControls;
for(iRows=0;iRows<ts->Rows->Length;iRows++)
{
  for(iControls=0;iControls<ts->Rows[iRows]->Controls->Length;iControls++)
    ControlToConfig(prefix+"Row"+iRows.ToString()+"Control"+iControls.ToString(),ts->Rows[iRows]->Controls[iControls]);
  iniFile->WriteString(form->Name,prefix+"NumRow"+iRows.ToString()+"Control",iControls.ToString());
}
iniFile->WriteString(form->Name,prefix+"NumRows",iRows.ToString());

En el código de arriba, prefix representa a una cadena indicando qué parte estamos guardando (Top, Left, Right o Bottom). Un rápido vistazo al fichero INI nos indica que para cada control se guarda su posición y su nombre, y como nombre a la izuqueirda del igual, su prefijo más la fila en la que se encuentra más la palabra "Control" más el número de control (por ejemplo, Top0Control1 se corresponde al control que está en el anclaje superior de la ficha, tira 0 y es el segundo control anclado en esa tira). Con un poco de perspicacia cualquier usuario avanzado es capaz de editar el fichero a mano y cambiar los valores que considere oportunos.

Cargar el estado es algo más complejo, puesto que no debemos crear los controles, sino que estos controles ya están en la ficha, por lo que es necesario buscarlos y reasignarlos.

Hacerlo de esta forma tiene algunas ventajs sobre la opción de crear los controles por programa, puesto que podemos editarlos y modificarlos sin problemas dentro del IDE y no es necesaria acción alguna cuando se arranque la aplicación por primera vez: los controles aparecerán situados en sus posiciones por defecto, y si por alguna causa se nos corrompe el archivo de configuración, con borrarlo tendremos un sistema funcional con los valores por defecto. Está claro que podríamos guardar todos los metadatos necesarios para crear el control en cuestión, pero consideramos que esta es la mejor forma.

Otra ventaja es que este código es general, tan solo requiere la llamada a los métodos ToolbarsToConfig cuando queramos guardar el estado y a ConfigToToolbars cuando queramos recuperarlo, eso sí, hay que hacerlo cuatro veces, una por cada ToolStripPanel que tengamos activo:

void FormMain::LoadToolbars(void)
{
  WindowConfig->ConfigToToolbars(this,"Top",m_mainToolStripContainer->TopToolStripPanel);
  WindowConfig->ConfigToToolbars(this,"Left",m_mainToolStripContainer->LeftToolStripPanel);
  WindowConfig->ConfigToToolbars(this,"Right",m_mainToolStripContainer->RightToolStripPanel);
  WindowConfig->ConfigToToolbars(this,"Bottom",m_mainToolStripContainer->BottomToolStripPanel);
}
void FormMain::SaveToolbars(void)
{
  WindowConfig->ToolbarsToConfig(this,"Top",m_mainToolStripContainer->TopToolStripPanel);
  WindowConfig->ToolbarsToConfig(this,"Left",m_mainToolStripContainer->LeftToolStripPanel);
  WindowConfig->ToolbarsToConfig(this,"Right",m_mainToolStripContainer->RightToolStripPanel);
  WindowConfig->ToolbarsToConfig(this,"Bottom",m_mainToolStripContainer->BottomToolStripPanel);
}

Observamos que al método correspondiente se le pasa una referencia a la ficha actual, el prefijo y el ToolStripPanel correspondiente. Pasar un puntero a la ficha no es necesario, puesto que podríamos utilizar el método FindForm del ToolStripPanel.

Estos dos métodos ha de implementarlos el usuario en cada una de sus fichas; el código de la clase WindowConfig (que tiene más cosas aparte de las mencionadas), se puede obtener aquí.

Una de cal y otra de arena. Pese a lo comentado hasta ahora, el sistema no funciona todo lo bien que debiera. De hecho, cuando hay más de una barra de herramientas en una misma tira, sólo se respetan sus respectivas posiciones en la tira número 0; en las demás se colocan siempre como quieren. El autor considera que se trata de uno de tantos bugs de los que el .NET Framework está plagado, puesto que las pruebas que ha realizado han sido bastante exhaustivas.

Entre ellas ha visto cómo al guardar una barra de herramientas, ésta tenía una localización de (300,50) y al ser cargada esa misma barra se negaba a aceptar ese valor, quedando en (300,0), lo ha visto incluso mediante el depurador en tiempo de ejecución, intentando introducir algo diferente a cero y viendo cómo la barra se negaba a aceptarlo. Se negaba tanto estando sin enlazar a ningún contenedor, como estándolo; tan sólo lo aceptaba si estaba enlazada en la tira 0. Y desde luego, una vez en tiempo de ejecución, esa barra ha podido ser movida satisfactoriamente.

Este problema se presenta tanto en el Visual Studio 2005 como en la beta de su SP.

jueves, noviembre 16, 2006

Ficheros INI de texto en C++/CLI

Recupero un artículo (recomiendo su lectura) ya viejo de otro de mis blogs en el que hablo de una clase que en su momento creé para trastear con ficheros INI al más puro estilo Win16.



El autor es de los clásicos, prefiere lo malo conocido a lo peor por conocer, y considera que el formato de los archivos INI es algo insuperable para guardar configuraciones. Combinado con el sistema de ficheros local por usuario de los últimos Windows (ya se sabe: C:\Documents and Settings\<user>\Local Settings...) forman una pareja insuperable a la hora de guardar pequeñas -o no tanto- opciones.



Es por ello por lo que en su momento creó una clase llamada IniStream capaz de simular el funcionamiento desde C++/CLI de estos ficheros, con la limitación de no permitir comentarios. Aparte de lo anterior, la clase contenía un bug estúpido que devolvía siempre el valor por defecto al leer un Int32, bug que descubrió al utlizar dicha clase en una de las aplicaciones que realiza en eso que sus jefes llaman trabjo y él pasarlo en grande (por favor, no se lo digan a mis superiores o lo mismo me bajan el sueldo).



Ahora, con un nuevo proyecto personal que lleva entre manos, se ha visto obligado -o más bien ha comprendido la necesidad de tener comentarios en ficheros que la gente podría leer- a añadir el soporte para los citados. Y ahora sí que ha comprobado que la clase funciona perfectamente, porque la ha incorporado al proyecto anterior y nadie se ha quejado (¿Será porque nadie usa ya esa aplicación?, agónica duda existencial) y al nuevo y ahora sí, funciona.



Se trata simplemente de una actualización, y el código se puede descargar de aquí.



Para aquellos demasiado jóvenes que desconozcan el formato de un archivo INI, diremos que se trata de un grupo de secciones que a su vez contienen valores, y estos valores están en la forma "nombre=valor". Un fichero podría estar compuesto así:




//Esto es un comentario
[Seccion1]
Elemento1=uno
Elemento2=dos
;Esto es otro comentario
[Seccion2]
Elemento1=1
Elemento2=2




Todo lo que no esté entre corchetes ni tenga un = de por medio se considera un comentario, al menos en mi especificación de fichero INI. Luego disponemos de métodos como




ReadString(String ^section,String ^name,String ^defValue);
WriteString(String ^section,String ^name,String ^value);




De este modo leemos un valor que se corresponda a un nombre de una sección dada, o lo escribimos. Podemos indicar una Sección por defecto si la asignamos a DefaultSection, y entonces ya no tenemos que especificar la sección en las llamadas. También podemos obtener un array con todos los nombres de todas las secciones, o todos los elementos de una de ellas, y soporta indexar mediante bucles con índice numérico y/o for each.



El funcionamiento es bastante sencillo, y utiliza genéricos CLI con diccionarios de datos. La clase contiene un diccionario de diccionarios, es decir, cada grupo de elementos de una sección dada está almacenado en un diccionario, y cada sección con su grupo en otro, de forma que podemos accecer a los datos así:




String ^value=iniStream[section][name];




Toda una gozada frente a otras formas de acceder, aunque sólo podamos leer cadenas de esta forma.

miércoles, noviembre 15, 2006

Volando voy, volando vengo, y en el camino me pego un hostión

Aunque parezca mentira, se trata Borland.

Sí, sí, de Borland.

Y se hacen eco varios blogs y noticieros del asunto. Y es que eso de ir dando tumbos es contraproducente.

Desde hace unos años Borland ha tratado a los programadores que usaban sus herramientas como mierdecillas, como ínfimos seres privilegiados capaces de usar sus fantásticas herramientas... en el caso de que sus herramientas lo hubieran sido, que lo fueron, pero que ahora caen en picado, sobre todo lo que tenga relación con C++.

Luego dio un tirón, sacó el BDS2006, volvió a dar otro tirón, re-sacó las Turbo, luego decidió venderlo todo, pero ahora resulta que no, que se lo queda y crea una nueva empresa llamada CodeGear. Por favor, hagan clic en el enlace, pártanse de risa ante la superweb y retornen aquí.

Y resulta que al leer el encabezado del DrDobbs (el segundo link de este documento, aunque fue el primero que yo leí), lo primero que me viene a la cabeza es: "joder, no han encontrado comprador", y parece ser que no soy el único. Lean los comentarios al blog de Steve Texeira, lean (el primer link de este documento).

Mal vamos, pero que muy mal, señor Borland.

lunes, noviembre 13, 2006

Cambiar el tamaño del disco duro virtual

Siguiendo con el tema de las máquinas virtuales, a muchos les habrá pasado que el disco duro virtual que en un principio les parecía enorme, se les ha quedado pequeño, y justo al revés, esas previsiones grandiosas de uso se han quedado casi en nada... Y con el tema de las activaciones no es cosa de ir tirando máquinas virtuales.

Cambiar el tamaño de un disco que no sea el del propio sistema operativo es trivial, creamos uno nuevo, lo añadimos a la máquina virtual y lo copiamos. El problema viene cuando se trata del disco del sistema operativo.

Voy a explicar cómo cambiar el tamaño de un disco duro virtual, ya sea estático o dinámico, tanto para hacerlo más pequeño como para aumentar su tamaño.

La primera tarea es tener una máquina virtual con un software de replicación o de copia de seguridad bueno, como puede ser el Acronis Disk Director o el Acronis True Image. El autor ha probado estos dos programas y con los dos ha podido realizar el cambio, así que supone que también será posible con cualquier otro software del mismo tipo.

La segunda es crear el nuevo disco duro con el tamaño deseado, ya sea dinámico o de tamaño fijo. Si se trata del True Image debemos tener un tercer disco de puente si no disponemos de suficiente espacio libre en la máquina virtual anterior.

La tercera es anexar tanto el disco duro virtual nuevo como el del que queremos cambiar su tamaño a la máquina virtual que hemos citado. En el caso de utilizar el True Image, también debemos conectar el de puente. Por descontado, el disco duro al que le queremos cambiar de tamaño también puede ser el de la propia VM lanzada.

Una vez dentro del sistema virtual, si tenemos el Disk Director u otro soft de clonado de discos el tema es sencillo: copiar el disco origen sobre el destino. Un buen soft de clonado no debe tener problemas a la hora de copiar sobre un disco de diferente tamaño: debe ajustarse perfectamente.

Si utlizamos el True Image o similar, el tema es algo más lento ya que precisamos la realización de una copia de seguridad completa (a nivel de disco) del disco origen sobre otro disco existente, que puede ser el de la propia máquina virtual copiadora si hay espacio o sobre otros diferente y temporal si estamos clonando la misma máquina virtual o no hay espacio en ella.

Una vez realizada esa copia de seguridad, debemos proceder a restaurarla sobre el nuevo disco. Aquí nos vale lo mismo que para el Disk Director. Si el software es bueno, no debe haber problemas.

Ahora viene el intercambio de discos. Nos vamos a la máquina virtual a la que le queremos cambiar el tamaño del disco, quitamos el viejo y añadimos el nuevo. Si arranca y todo va bien es el momento de borrar el disco viejo y el temporal, o guardar el viejo por si acaso o para utilizarlo en otra VM.

Optimizar el rendimiento de una Máquina Virtual

Yo sigo con mis tejemanejes en relación a las máquinas virtuales, intentando sacarles el mayor rendimiento.

Como en mi máquina tengo Windows XP x64 en un principio me vi obligado a trabajar con el Virtual Server 2005 R2, luego pasé a la beta del SP1, y cuando salió el Virtual PC 2007 Beta, cambié a éste. Y ahora he vuelto de nuevo al Virtual Server R2 SP1 beta, que en mi opinión de beta tiene poco pues funciona inmejorablemente.

Y todo por el tema del rendimiento. He preguntado y leído montones de documentos, y según de qué lado estuvieran, he recibido informaciones contradictorias y en general completamente incorrectas, sobre todo cuando dejaban de comentar las cosas evidentes. Y también entre toda la paja existente he encontrado la aguja (gracias, Tella).

Al final todo queda resumido en tres reglas básicas:
  1. Quita todos los elementos gráficos accesorios. Vete a Mi PC, Propiedades, Rendimiento, y selecciona "Ajustar para mejor rendimiento". Desaparecerán todas las patochadas gráficas y quedará un sistema sobrio.
  2. Los discos virtuales han de estar situados fuera de los discos duros en los que se encuentre la partición de arranque y de paginación, y si puede ser, en discos duros situados en una controladora aparte o, mejor, RAID. Ojo, estoy hablando de discos duros físicos, no particiones.
  3. Los discos duros virtuales han de ser de tamaño fijo, con la controladora SCSI de la máquina virtual, y el fichero debe estar desfragmentado desde el ordenador anfitrión. Nada de compresiones ni chorradas de esas.

Con estas tres reglas de oro podemos aumentar significativamente el rendimiento de una máquina virtual.

1.- Eliminar todos elementos gráficos accesorios. El punto 1 es evidente por sí mismo, cuantas menos chorradas, más rendimiento.

2.- Discos virtuales en otro disco físico y/o controladora. Este también es casi evidente por sí mismo. Una máquina virtual genera un tráfico bastante intenso en su disco duro virtual, sobre todo de lectura. Si sumamos ese tráfico al que genera el propio sistema operativo, el rendimiento cae en picado.

El rendimiento mejora cuando el disco duro virtual se coloca en un disco duro aparte, porque dividimos entre dos el tráfico de datos sobre una unidad, de modo que las cabezas de lectura/escritura se han de mover en menor medida. Si el disco duro está en el mismo cable, la mejora no es muy sustancial porque todavía es necesario que un disco duro espere al otro, o mejor dicho, el sistema debe acceder primero a un disco y luego al otro.

Separando entre cables las dos partes interesadas volvemos a ganar algo más de rendimiento, pues ahora el acceso a ambos discos duros puede resultar casi simultáneo, y si a su vez dichas partes están separadas entre controladoras, mejor todavía, pues ahora el acceso sí que es completamente independiente, puesto que mientras el sistema accede a una controladora, la otra está trabajando.

Podemos todavía mejorar más el rendimiento si colocamos los ficheros de los discos duros virtuales en sistemas RAID en los que se utilicen varios discos duros simultáneamente para repartir la información.

Otra consideración a tener en cuenta es la cantidad de máquinas virtuales que vamos a tener lanzadas simultáneamente. Si podemos, cada disco duro virtual de cada máquina simultánea, ha de estar situado en diferentes discos físicos, para aumentar el rendimiento.

No vale mencionar el hecho de colocar cada cosa en varias particiones de un mismo disco duro físico, puesto que ahora estamos disminuyendo todavía más el rendimiento, pues obligamos a los cabezales a ir de un lado a otro alternativamente. Si no tienes la posibilidad de tener varios discos físicos, lo mejor es dejarlo todo en la misma partición y desfragmentar muy regularmente.

Ya por último, la controladora virtual SCSI de Microsoft tiene un desempeño mucho mejor que la IDE; ignoro el motivo, puesto que al final, el fichero que representa el disco duro virtual tiene exactamente el mismo formato en ambos casos, pero está lo sufientemente contrastado que la controladora SCSI es mucho más rápida que la IDE... De ahí mi vuelta al Virtual Server.

3.- Discos de tamaño fijo sin comprimir. Este quizás sea el tema más controvertido de todos, y el más difícil de explicar, por lo que iremos por partes.

3.1.- Discos de expansión dinámica frente a fijos. Un disco de tamaño fijo queda representado como un archivo de tamaño fijo en el sistema operativo anfitrión; esto significa que una vez desfragmentado desde fuera, todas las escrituras que se hagan desde dentro de la máquina virtual no van a afectar al rendimineto, puesto que al tener el tamaño total, cuando desde dentro se intente escribir en un espacio nuevo, ese espacio ya estará presente en el archivo, de forma que, externamente, no hay ningún cambio si no es la reescritura de un sector físico que antes contenía ceros por los nuevos valores.

Este funcionamiento no fragmenta un archivo. Es decir, escribir en una parte ya existente de una archivo no cambia su fragmentación, ésta sólo cambia si cambia el tamaño del mismo, aunque generalmente sólo si se incrementa.

Otra ventaja de un archivo de tamaño fijo desfragmentado consiste en que todo el espacio está contiguo, de modo que la escritura de un grupo de sectores es mucho más rápida que si se precisa que las cabezas vayan de un lado para otro, ya que aquí el tiempo de escritura es el mismo, lo que varía es el tiempo de seek o posicionamiento.

Un archivo dinámico tiene la contrapartida del aumento de la fragmentación, tanto externa como interna. La representación física del disco duro dentro de un archivo estático es lineal, de cero al tamaño total; la representación en uno dinámico es relativa, tan sólo existen los sectores escritos alguna vez. Cuando el sistema operativo virtual necesita un nuevo sector, ése se graba a acontinuación de los ya existentes.

Esto genera una doble fragmentación, la interna que consiste en que los sectores podrían estar desordenados con la consecuente pérdida de tiempo en su búsqueda, y la externa, porque el archivo crece y podría no existir tamaño contiguo suficiente, fragmentando el mismo.

La ventaja real de archivo estático frente al dinámico consiste tan solo en el nivel de fragmentación, que en el caso de unos pocos accesos es insignificante, pero que en accesos continuados puede llegar a reperesentar una mejora drástica, superior incluso a la de un ordenador real.

Son los tiempos de movimientos de cabezales de lectura/escritura los que determinan en todo momento el rendimiento. La cantidad de información a leer o escribir es la misma, pero si los datos están más cerca unos de otros, el tiempo es menor. Y el comportamiento se puede ver a simple vista, con los discos para deshacer.

Pon a tu máquina virtual ese tipo de discos y compara el rendimiento (y el ruido que emite el disco duro). Ahora podemos comprobar prácticamente los efectos de la fragmentación. En este caso tenemos dos ficheros diferentes, generalmente muy separado uno de otro físicamente...

3.2.- Discos comprimidos frente a discos sin comprimr. Esta es quizás la explicación más difícil de todas, así que vamos a hacerlo mediante un ejemplo.

Imaginemos que tenemos una máquina virtual almacenada en un disco virtual de tamaño fijo pero que está comprimido por fuera (es decir, es el archivo el que está comprimido, no la partición interna de la máquina virtual).

Ahora supongamos que estamos realizando una operación dentro del sistema operativo virtual, algo que determine que un fichero existente cambie de tamaño y a una tasa de compresión menor. Dentro del sistema operativo virtual, el nuevo fichero se alojará sobre el anterior, y nada más.

Pero fuera ocurrirán extraños efectos. Un pedazo del fichero que antes ocupaba, por poner un ejemplo, 4K, pasa ahora a ocupar 5k, con lo que aumentamos un sector más. ¿Cómo responde a esto el sistema? Pues la solución más sencilla es insertar un sector nuevo, cosa nada trivial cuando se trata de un fichero grande.

El sistema operativo podría mover todo el fichero, desde el final hacia atrás hasta hacerle hueco. Imagina que el ordenador tiene que mover un fichero de dos gigabytes...

Pero no es así como ocurre realmente. Lo que ocurre es que el archivo se fragmenta. Un archivo que antes estaba de una pieza, ahora está partido en tres. La primera llega hasta el sector modificado, la segunda es el sector añadido (en general lejos del archivo original, sobre todo si hay muchos de gran tamaño, como suele ser habitual en particiones que contienen máquinas virtuales) y la tercera el resto del fichero.

Imaginemos ahora la operación contraria, que un archivo en la máquina virtual ocupe menos espacio que antes. Ahora, lo que tenemos en el archivo externo es un hueco, que las cabezas deben saltar...

Ahora repitamos el procedimiento un rato. ¿Qué nos queda? Un sistema enormenente fragmentado, el más fragmentado de todos, más fragmentado todavía si la máquina virtual está en un archivo fijo.

Sólo nos queda determinar qué ocurre con el sistema operativo virtual comprimido en un archivo fijo. Aquí el autor considera que podría obtenerse alguna mejora en el rendimiento, ya que el tiempo de proceso de comprimir es menor que el de escribir la parte no comprimida, pero prácticamente no ha encontrado diferencia significativa, quizás porque el tiempo de compresión sea más o menos igual al del ahorro en la escritura, al menos en su máquina.

sábado, noviembre 11, 2006

Búsquedas binarias y ordenaciones en .NET

Una de las enormes ventajas del .NET es la facilidad con la que se realizan tareas que de otro modo hubieran llevado mucho más tiempo... aunque a veces descubrir cómo funcionan supone gastar casi más tiempo que el supuestamente ahorrado.

Todas las clases que heredan de la clase abstracta Array tienen la propiedad de realizar búsquedas binarias y de ordenarse a sí misma. Este es un aspecto bastante interesante, porque podemos tener casi cualquier objeto enlazado, heredado o contenido dentro de sus herederos, como es el caso de ArrayList, que puede contener un array con elementos de cualquier otro objeto.

El mantener un array ordenado de cadenas es algo trivial, y de hecho existe una clase para ello, StringList. Pero tener un array ordenado de objetos más complejos es harina de otro costal, sobre todo a la hora de ordenarlos... más que nada por la decisión de qué elemento dentro del objeto es el que va a generarnos la ordenación. En el mundo nativo, en C++, existe la función qsort a la que se le pasa un puntero a función que devolverá cuál de los dos elementos pasados es mayor, y así, qsort será capaz de ordenarlos realizando repetidas llamadas a esa función.

Pero en el mundo .NET eso está prohibido, aparte de que es un potencial fallo de seguridad, de contención de memoria y.. y.. casi de todo. En el mundo .NET podemos pasar un comparador, de forma parecida a como se hace en la STL.

La clase Array dispone de un método Sort sobrecargado con diferentes parámetros. A nosotros nos interesa aquellos a los que podemos pasar un comparador, o mejor dicho, un objeto heredado del interfaz IComparer. El método BinarySearch también puede recibir varios parámetros y, al igual que el anterior, también podemos pasarle un comparador.

Este "comparador" es el elemento que determinará cómo se realizará la ordenación, así como el resultado de las búsquedas binarias realizadas. Pero hagamoslo con un ejemplo sencillo.

Supongamos que tenemos una estructura que contiene una cadena y un valor que se corresponde con un índice de otro sitio. En lugar de definirla del modo clásico, lo haremos así:

ref struct SectionItem:System::Collections::IComparer
{
String ^SectionName;
int Line;
virtual int Compare(System::Object o1,System::Object 02)
{
SectionItem ^s=(SectionItem ^)o1;
return String::Compare(s->SectionName,(String ^)02,true);
}
};

Tenemos una estructura que posee un método Compare que se encarga de realizar la comparación respecto al nombre. Ahora, ordenar por el nombre o realizar una búsqueda binaria es un asunto bastante trivial. Supongamos que sections es un ArrayList que contiene una serie de objetos del tipo SectionItem.

System::Collections::IComparer ^comparer=gcnew SectionItem;
sections->Sort(comparer);
int index=sections->BinarySearch("valorPrueba",comparer);

Hemos definido el comparador dentro de la definición de la propia estructura, pero nada ni nadie nos impide crearnos nuestras propias clases herederas del interfaz IComparer independientes de la estructura y que sean capaces de realizar comparaciones con los criterios que consideremos oportunos.

Carl von Clausewitz: La campaña de Rusia

RBA Coleccionables, 2005
The Campaign of 1812 in Russia
84-473-4606-4

Este libro ni fu ni fa, por lo menos a mi. Demasiado breve, limitado en exclusiva al desarrollo, a veces excesivamente árido, a veces excesivamente numérico, nunca profundo y pocas veces descriptivo.

viernes, noviembre 03, 2006

Benito Pérez Galdós: Episodios Nacionales primera serie (y II)

0100/2006 - Gerona: Esta novela, aunque narrada por boca de Don Diego, no cuenta sus aventuras, sino las de uno de sus amigos, que nos narra el sitio de Gerona en toda su crudeza; en esta novela asistimos, por un lado a escenas de batallas heroicas, brevemente pinceladas, y por otra a escenas de una crudeza sin igual en Galdós (por lo menos hasta donde he leído yo), contando qué hacía la gente para conseguir comida, matándose unos a otros o comiendo cosas de lo más inmundo (aunque personalmente no veo qué tiene de malo una rata bien hecha, pero bueno). Como curiosidad, comentar que casi en el final de la novela se narran una serie de combates entre varios grupos de ratas, como metáfora de lo que realmente ocurrió y Galdós, quizás por censura, o por temor, no se atreve a poner en boca de sus personajes.

0096/2006 - Cádiz: De la contraportada de la edición de Cätedra: : Los Episodios Nacionales de Benito Pérez Galdós constituyen un friso histórico que recrea la historia de España desde la batalla de Trafalgar (1805) hasta los primeros años de la Restauración. Cádiz es el octavo relato de la primera de las cinco series que integran los Episodios. El tono moderadamente optimista de la primera serie responde al intento de vincular la constitución de la nueva clase burguesa, de marcado carácter progresista, con la creación ideológica del concepto de nación y patria en un sentido moderno. Pero en la escala valorativa de Galdós la novela está un punto por encima de la Historia y Cádiz marca un hito en esta línea continuada de evolución conceptual. Si bien la fidelidad del autor a las fuentes historiográficas es desigual a lo largo de los distintos momentos del relato, resulta, sin embargo, difícil imaginar cómo en un único episodio se podría ofrecer un panorama más sintético y exacto del proceso político vivido por la nación simultáneamente a la Guerra de la Independencia

0101/2006 - Juan Martínez el Empecinado. En este penúltimo episodio Galdós nos cuenta un poco sobre la vida de guerrillas en la época de la conquista napoleónica. Nuestro Araceli es destinado a recorrer el monte emboscando a los franceses y siendo testigo de las desavenencias internas y de los cambios de chaqueta que muchos luchadores hacían según fueran los tiempos. Nos cuenta las hazañas de Juan Martínez y de algunos otros. Al final termina capturado por los francess y justo cuando lo van a fusilar consigue huir, y es cuando Galdós nos relata más cosas sobre las aventuras de Araceli con su prometida y del rapto que hace de ella su padre, afrancesado.


0102/2006.- La batalla de los Arapiles. Con esta novela comienza la caída de la invasión napoleónica en España, así como también las aventuras de Araceli llegan a un principio de buen fin. Nuestro personaje va subiendo en el escalafón militar, llegando a codearse con el artífice de la victoria de los Arapiles, Wellington. Se ofrece voluntario para infiltrarse en la por entonces fortificada ciudad de Salamanca, controlada por los franceses, como medio de buscar a su amada. Tras conseguir los datos necesarios para la toma de la ciudad, pero fracasando en su intento de recuperar a su nova, se produce la batalla, en la que Gabriel consigue conquistar una bandera al enemigo pero es herido y está a punto de morir. Al final, una vez recuperado, consigue la mano de la chica. Y no olvidemos a Miss Fly.

Prueba pruebosa

Prueba pruebosa