Vous êtes sur la page 1sur 97

Grupo Albor

Una publicacin del Grupo Albor Revista Sntesis N 20

Un lugar de encuentro para los programadores de habla hispana

Informtica y Salud
por Demetrio Quirs

Frente a un teclado y una pantalla nuestra salud tambin puede verse comprometida. Conocer los riesgos es el primer paso para evitarlos

Y, adems...
Introduccin a Report Manager, por Magoo. Introduccin a la Gestin de Proyectos, por Sebastin Silva. Creacin de Sitios Webs Dinmicos, por Ricardo Pereyra.

Contenedores Asociativos (y IV)


La culminacin de la serie sobre contenedores asociativos, esta vez haciendo foco en los llamados Contenedores Asociativos de Clave Mltiple por Mario Rodrguez

El Depurador de Delphi
En la seccin dedicada a este compilador presentamos una introduccin al depurador de Delphi 6. Eliminemos los bichos con el depurador integrado por Tavo Ibaceta

http://www.grupoalbor.com/

Contenido
Sntesis
Nmero 20 - Junio de 2006

Director Tavo Ibaceta


3 Presentacin

Coordinador de redaccin Ricardo Pereyra


Colabora en revisin tcnica

5 16 33

Informtica y salud
Demetrio Quirs

Creacin de un sitio web dinmico I


Ricardo Pereyra

Santiago Martinez

Mxima compresin con 7-Zip


Mario Rodrguez

La revista Sntesis no se hace responsable ni de la exactitud en el contenido de los artculos, ni de las opiniones vertidas por los autores de los artculos. Tampoco se responsabiliza de cualquier posible dao producido por el uso de los ejemplos proporcionados. Tampoco se incluyen referencias a las "marcas registradas" habida cuenta de la aparicin continuada de las mismas a lo largo de la publicacin.

37 67 77 85

Contenedores asociativos (y IV)


Mario Rodrguez

Introduccin a Report Manager


C. E. Rodrguez (Magoo)

El depurador de Delphi (I)


Tavo Ibaceta

Introduccin a la Gestin de Proyectos


Sebastian Silva

Revista Sntesis es una publicacin gratuita del Grupo Albor

revista_sintesis@grupoalbor.com

Revista Sntesis - Junio de 2006

Por la noche vino el seor Borland...


Se present con unos seores de negro, pareca preocupado, con miedo. Me dijo -"Deberas actualizar a Delphi.NET"-. Le dije -"Me habas dicho que apueste a CLX para poder entrar en el mundo Linux. qu pas?"-. "Eso es el pasado!. El futuro ahora es .NET!", me dijo, ms enftico. El de anteojos, desde atrs, daba rdenes en voz baja. -El proyecto Lazarus.....- comenc a hablar. Pero se sobresaltaron demasiado, yo me asust tambin. -NOO!!! No digas eso- Dijo el seor Borland. Los de negro haban llevado la mano dentro a la chaqueta. - No vuelvas a decirlo implor. En su mirada aterrorizada haba cierto miedo por m. -Debes... deberas actualizar a Delphi.NET-. Su ojos pedan por favor. El de anteojos dijo algo en voz baja, lo tomaron del brazo y se lo llevaron. El seor Borland mir hacia atrs con lgrimas de impotencia. No s si volver a verle....
objetivo histrico del Grupo responsable de esta revista que hoy vuelve a ver la luz fue convertirse en el lugar de encuentro de los programadores hispanos de los productos Borland. Intentando alcanzar ese objetivo y encauzar las inquietudes de muchos de los usuarios de los compiladores de dicha casa, chocamos de bruces -y muchas veces- contra esa pared denominada "lgica comercial". La lgica comercial de Borland. No es ningn secreto que la poltica actual de Borland no es del agrado de todos los que alguna vez estuvimos conformes con sus productos. Aquellos desarrolladores que -como un ejemplo- se quedaron en Delphi porque creyeron el argumento de Borland de que Kylix sera la mejor herramienta para entrar a Linux, ahora -con su compilador discontinuado- son llevados casi a la fuerza a las garras de .NET. Qu motivos habra para actualizar ahora a Delphi.NET? El nico importante sera que hay muchas aplicaciones escritas en Delphi y cuesta bastante reescribirlas en otro lenguaje. Actualizar, en este caso, sera para aprovechar la VCL.NET y ahorrar costos de migracin ante una imposicin inevitable? del sistema operativo del futuro. Puede decirse que, en el momento en que este grupo se cre, la dicotoma del programador con espritu crtico era Microsoft o Borland. Ahora las cosas han cambiado. Ambas empresas han decidido aunar esfuerzos (y poco importa quin compr a quin) declarando la guerra a un nuevo monstruo denominado software libre, que amenaza la continuidad de sus pinges negocios. Si alguna vez pudimos optar por los productos Borland por su calidad, ahora esa eleccin es lamentablemente mas compleja que la mera calidad de los compiladores. La discusin sobre software "propietario" software libre ha llegado para quedarse y, desarrolladores de software como somos, sentimos la obligacin de defender y apoyar tambin a ste ltimo, que consideramos no slo maduro sino tambin productor de software de muy alta calidad, sobradamente probada en diversos productos. Ello no significa que los productos Borland hayan descendido de calidad. Tampoco que vayamos a abandonar su tratamiento en nuestros foros, sede o publicaciones. Como programadores, seguiremos utilizando las herramientas que hemos estado usando hasta la fecha, hablando de ellas y solventando los Revista Sntesis - Junio de 2006 3

El

problemas del da a da. Pero, a la par, estaremos abiertos a todas las alternativas existentes que provengan del software libre que, en muchos casos, son de mayor calidad que las comerciales. Junto a ello, hemos recapitulado todos los vaivenes que ha tenido la poltica de Borland durante los ltimos tiempos: Kylix, C++BuilderX, el intento de abandono de C++Builder,... Esas idas y venidas nos perjudican a todos, desde aquellos que compran productos nuevos, como Kylix, con la esperanza de que se contine su desarrollo, hasta aquellos que actualizan a (y, por tanto, pagan) nuevas versiones y se encuentran con que, al cabo de unos meses, se anuncia el abandono del desarrollo. En ese contexto, seguir desarrollando segn las lneas marcadas por Borland (VCL, CLX, .NET,...) puede significar que, a la larga, muchos proyectos puedan no tener continuidad. En cualquier momento Borland puede dejar de desarrollar un producto (vase el BDE o Kylix), sin que parezca importarle excesivamente lo que piensen sus clientes: nosotros. O venderlo directamente, como est haciendo ahora con todos sus entornos de desarrollo. No pensamos que exista tal guerra entre software libre y propietario, lo que s existe es una intencin deliberada de ste ltimo en desprestigiar al primero. Apoyamos al software libre, no porque queramos estar del lado de, sino porque consideramos que trabajar con software libre es hoy garanta de calidad, de continuidad y por qu no? de portabilidad al margen del capricho de ninguna compaa. Las compaas suelen proporcionar una cierta estabilidad a los desarrollos, pero slo mientras dure su inters comercial en ellos. Quienes hayan desembolsado unos chelines en Visual J o Kylix tendrn una idea del asunto. El Grupo Albor (ya se ha dicho muchas veces en estas pginas) es independiente de cualquier casa de software. No estamos de acuerdo con la poltica comercial de Borland y, por ello, sera injusto para con la comunidad seguir apoyando slo los desarrollos comerciales de dicha casa. Por todo ello, el Grupo Albor -que no es Borland sino de los programadores- decide abrir sus puertas al software libre. Las actividades del grupo contemplarn, en esta nueva etapa, toda actividad referida a cualquier compilador, IDE, biblioteca, etc., que se encuentre desarrollada bajo alguna de las licencias del "software libre", tanto de GNU (GPL, LPGL,...) como de las cercanas (Netscape: NPL, Mozilla: MPL, Apache: ASL). Las listas de discusin, la sede y las publicaciones del Grupo Albor dejarn de ser exclusivamente sobre productos Borland. La revista Sntesis, ltima de nuestras actividades en llegar al software libre luego de nuestra sede, las listas de discusin y los foros en lnea, reaparece con el espritu de siempre: libre y totalmente gratuita. Si desea apoyar la iniciativa puede contactarse con los responsables de la publicacin escribindonos un correo a revista_sintesis@grupoalbor.com.

Tavo Ibaceta

Revista Sntesis - Junio de 2006

Demetrio Quirs Santos - demetrio@grupoalbor.com

Salud

Informtica y salud
Frente a un teclado y una pantalla nuestra salud tambin puede verse comprometida. Conocer los riesgos es el primer paso para evitarlos
Qu profesiones de riesgo conoces? Si hacemos esta pregunta a compaeros, amigos o familiares nos sorprenderemos escuchando prcticamente las mismas respuestas. Nuestra primitiva percepcin del riesgo va ntimamente asociada a la posibilidad de que un accidente grave pueda ocurrir: cadas desde un andamio, la trituradora de plsticos que devora al operario o una sierra elctrica secciona un brazo. Todos estos accidentes pudieron haberse evitado respetando las medidas de seguridad y empleando los medios de proteccin adecuados. Pero existen otras actividades que, sin entrar en el catlogo popular de profesiones de riesgo, constituyen una importante fuente de lesiones en quienes las practican. Su peligrosidad radica, precisamente en lo sutil de su actuacin y lo lento que van horadando la salud hasta presentarse, un buen da, en forma de lesiones invalidantes e irreversibles. Y una de esas actividades la est realizando en este preciso momento: estar sentado tranquilamente frente a la pantalla, una mano sobre el teclado, otra sobre el ratn.

Breve historia
La relacin entre las actividades profesionales y sus efectos sobre la salud de los trabajadores no es algo novedoso. Platn, en el siglo V a.c., describe determinadas enfermedades producidas a causa de los trabajos en las minas. Hipcrates y Galeno hicieron lo propio con las patologas derivadas de los trabajos de extraccin de mineral de plomo. Pero el concepto de higiene laboral se debe a Bernardo Ramazzinni, quien en su obra fechada en 1.690 De Morbis Artificium Diatriba, describe hasta 54 profesiones y sus riesgos asociados, proponiendo medidas para evitarlos. La revolucin industrial deriv en el aumento de las enfermedades profesionales y accidentes de trabajo. Aparecen los primeros movimientos sindicales y en algunos pases comienza a tomarse el problema en consideracin. La creacin de la OIT (Organizacin Internacional del Trabajo) en 1919 supone un importante avance, promoviendo la redaccin de convenios y normativas internacionales.

Figura 1 - Luminaria tipo downlihgt

Revista Sntesis - Junio de 2006

Informtica y salud

Salud

Ya ms recientemente, en el mbito de la Comunidad Europea, se promulgan algunas directivas que establecen criterios de carcter general sobre diferentes materias en el mbito de la seguridad y la salud en el trabajo, siendo la Directiva 90/270/CEE la que establece las disposiciones mnimas de seguridad y salud relativas al trabajo con equipos que incluyan pantallas de visualizacin de datos. Con la publicacin del RD 488/1997 se procede a la transposicin al Derecho espaol de la mencionada directiva europea. Pero en esta ocasin no abordaremos el tema desde un punto de vista legislativo, distinto en cada uno de los pases donde nos encontremos, sino que nos acercaremos a l de una manera prctica, enumerando los diferentes problemas que podemos encontrarnos y la manera de minimizarlos.

Los trabajos con ordenador y sus riesgos asociados


La rpida introduccin de las herramientas informticas y su utilizacin en los ms variados entornos ha derivado en la aparicin de numerosos estudios que las relacionan con diversas patologas. As podemos citar:

Alteraciones msculo esquelticas Alteraciones visuales. Trastornos por carga o fatiga mental.

Figura 2 - Asiento con reposabrazos regulables en altura, longitud y ngulo. Respaldo con soporte lumbar regulable.

Alteraciones msculo esquelticas


Quiz sean los problemas ms frecuentes que se manifiestan en los trabajos de oficina, en general. El mantenimiento de una misma postura durante largos perodos de tiempo provoca fatiga muscular, por inmovilizacin, que se manifiesta con dolor. Las zonas ms afectadas son la columna vertebral, cervicales y dorsales y las extremidades superiores. Estando sentados, la columna permanece erguida, los hombros tienden a descender de su posicin natural y el cuello adopta inclinaciones que fuerzan las cervicales. La posicin inadecuada de las muecas sobre el plano de trabajo y el desplazamiento que la mano realiza desde el teclado al ratn hace que, cada vez con mayor frecuencia, se describan casos de Sndrome del Tnel Carpiano, Tenosinovitis de Quervain e Higromas o bursitis de las bolsas sinoviales en los tendones de la mano. En menor medida se observan problemas circulatorios en miembros inferiores, con la aparicin de varices, sensacin de piernas cansadas y en algunos casos, estasis venoso.

Sndrome de tnel carpiano: compresin del nervio mediano a su paso por el carpo de la mueca que provoca hormigueo, prdida de fuerza y dolor en la mano. Tenosinovitis de Quervain: estrechamiento de las vainas de los tendones del pulgar, provocando dolor en la cara externa de la mueca y el pulgar. Los movimientos incrementan el dolor. Higromas o bursitis: inflamacin de las vainas que recubren los tendones. Estasis venoso: circulacin sangunea lenta y difcil. Tendinitis: inflamacin de los tendones

Revista Sntesis - Junio de 2006

Informtica y salud

Salud

Alteraciones visuales
La observacin continua de la pantalla tiene efectos muy negativos sobre nuestra visin. Cuando fijamos la vista sobre un objeto nuestra frecuencia de parpadeo disminuye. Ahora que lo ha ledo quiz haya parpadeado, pero pronto volver a dejar de hacerlo durante un buen rato. Cuando el tiempo transcurrido entre parpadeos es mayor que el tiempo que tarda la pelcula lagrimal que refresca nuestros ojos en secar, se produce enrojecimiento, sequedad ocular y visin borrosa. El excesivo contraste entre la luz ambiente y la emitida por la pantalla fatiga en exceso la musculatura ocular, que debe adaptarse continuamente a las condiciones lumnicas. Si, adems, debemos dirigir la mirada hacia puntos que no se encuentran a la misma distancia durante cortos periodos de tiempo, obligamos a los ojos a modificar continuamente el enfoque. Como resultado podemos padecer cefaleas, vrtigos o doble visin, entre otros.Trastornos por carga o fatiga mental El trabajo de creacin de un programa de ordenador puede llegar a ser una actividad peligrosa para nuestra salud mental. Los principales sntomas asociados a los trastornos por carga o fatiga mental son los estados de ansiedad, irritabilidad, insomnio o sueo agitado.

Figura 3 - Obsrvese la diferencia de contraste entre la pantalla y la luz exterior

Si en los programadores independientes o de pequeas empresas los problemas surgen por la excesiva carga de trabajo, los cortos plazos de entrega y la alta responsabilidad, en aquellos que forman parte de un amplio grupo de trabajo en grandes empresas es la excesiva especializacin la causa principal del agotamiento mental, debiendo favorecerse en estos casos la rotacin de equipos.

Ergonoma y diseo del puesto de trabajo


La ergonoma es una ciencia relativamente moderna que el diccionario de la RAE define como el Estudio de datos biolgicos y tecnolgicos aplicados a problemas de mutua adaptacin entre el hombre y la mquina. En otras palabras, se trata de estudiar a las personas para adaptar las mquinas, herramientas, enseres, mtodos y procedimientos. En nuestro caso los criterios ergonmicos a considerar abarcan tanto las caractersticas del mobiliario y equipamiento informtico como las condiciones ambientales. Y no menos importante es cmo hacemos uso de estos elementos. Un excelente asiento y una sala con la temperatura adecuada pueden convertirse en nuestros peores enemigos si el asiento no est correctamente regulado y la salida del aire acondicionado cae directamente sobre nuestra cabeza.

Mobiliario
El asiento es, sin duda, el elemento ms importante en el diseo del mobiliario. Debe ser estable, con cinco patas y ruedas giratorias que faciliten el movimiento. La banqueta debe tener su borde exterior inclinado hacia

Revista Sntesis - Junio de 2006

Informtica y salud

Salud

abajo. El respaldo debe poder regularse en altura e inclinacin para adaptarse al contorno de la espalda, siendo preferibles los respaldos altos con ajuste lumbar, esto es, una protuberancia a la altura de las lumbares que se ajustan a la curvatura de la espalda y proporciona una mejor sujecin de toda la columna. En los trabajos con teclado los reposabrazos tienen especial importancia. Un asiento ideal debe permitir la regulacin en altura, apertura y longitud. En funcin de la estatura del usuario, puede ser aconsejable la utilizacin de un reposapis inclinado. La mesa, de dimensiones suficientes para que puedan disponerse todos los elementos necesarios, debe contar con una superficie mate para evitar reflejos, de colores claros y bordes redondeados. Se deben evitar las conexiones elctricas bajo la mesa, ya que constituyen un riesgo de descargas. Las piernas deben poder moverse con facilidad. Si se suele trabajar con libros, publicaciones o impresos, debe disponerse de un atril portadocumentos. Conviene, adems, que el nivel de iluminacin y el color del fondo de pantalla del programa que est empleando sean similares al del documento.

Ambiente
Los trabajos sedentarios en interiores precisan de Figura 4 - Utilizacin de medios inadecuados que unas determinadas condiciones ambientales de elevan de manera excesiva la altura del monitor, temperatura, humedad, y calidad del aire. Un mayor incrementando su altura respecto del atril. nivel de esfuerzo fsico en el trabajo requiere de temperaturas y grados de humedad ms bajos y se toleran corrientes de aire de mayor velocidad. En nuestro caso una temperatura ambiente de entre 18 y 24, con humedades relativas de entre el 40% y el 70% pueden considerarse adecuadas, aunque una temperatura de entre 20 y 22 sera la ptima. El aire en circulacin no debe superar la velocidad de 0,1 m/s (el humo de un cigarrillo subira en vertical, a ms de 0,3 m/s se inclinara, a partir de 0,5 m/s comenzaran a volar los papeles). Otro parmetro de suma importancia y que precisa de aparatos de medida especiales es la cantidad de CO2 (dixido de carbono) presente en el aire. En salas con una alta densidad de trabajadores por metro cuadrado o donde deba atenderse al pblico y que carezcan de sistemas de ventilacin o estos sean ineficaces es ms fcil de lo que pueda pensarse que lleguen a superarse los lmites mximos de CO2 permitido. Esto provocara una disminucin de la capacidad de concentracin, aumento de la frecuencia de respiracin, cansancio y dolores de cabeza.

Figura 5 - Teclado con bloques claramente diferenciados y una correcta inclinacin.

Revista Sntesis - Junio de 2006

Informtica y salud

Salud

Por ltimo, debe cuidarse la cantidad y la calidad de la luz presente en la sala. Siempre que sea posible es preferible la luz natural, en otro caso, luminarias tipo downlight son adecuadas ( Figura 1). La cantidad de luz adecuada para trabajos en oficinas est en valores de unos 750 a 1.000 lux. Su distribucin debe ser uniforme para no provocar zonas de sombra o diferentes contrastes.

Equipamiento
Resulta sorprendente comprobar cmo en la mayora de las ocasiones los elementos que nos van a servir de comunicacin directa con nuestra principal herramienta de trabajo son los menos valorados: pantalla, teclado y ratn. Si queremos cuidar nuestra salud visual resulta primordial disponer de una pantalla apropiada. Aunque en los modernos monitores LCD ya no tiene sentido, en los tradicionales de tubo de rayos catdicos la frecuencia de refresco de la pantalla es de suma importancia. Valores por debajo de 70Hz provocan un parpadeo, aunque a penas imperceptible, a la larga deviene en mareos y cefaleas debido al agotamiento ocular al que nos somete la actualizacin de la pantalla a tan baja velocidad. Los valores ptimos se encuentran a partir de 75Hz, debiendo seleccionar siempre la mayor frecuencia que soporte la tarjeta grfica y el monitor.

Modificacin de la frecuencia de refresco. En Windows, desde el men de Inicio: InicioConfiguracinPanel de ControlPantalla (Figura 9) Seleccionar la frecuencia ms alta soportada por nuestro monitor. En Windows NT, tendremos un botn 'Prueba' para comprobar si la frecuencia es, efectivamente, tolerada. En Windows XP el sistema cambiar a un cuadro de dilogo en el que debemos aceptar la nueva configuracin. En Linux, comprobar el archivo de configuracin del sistema X. En sistemas con Xfree86 ser /etc/X11/XF86Config y en aquellos con Xorg /etc/X11/xorg.conf. Dentro de la seccin [Monitor] estableceremos la opcin VertRefresh a los valores soportados por el modelo de monitor:
Section [Monitor] ... VertRefresh 75-85 EndSection

Un teclado de calidad, con unas grfica para modficar la frecuencia de refresco de la pantalla. (Figura 10) dimensiones y separacin entre teclas adecuadas, una correcta inclinacin de las filas superiores respecto de las inferiores, los distintos bloques funcionales claramente diferenciados con una separacin de, al menos, la mitad de la anchura de una tecla y un desplazamiento suave al teclear no slo facilitar su utilizacin sino que favorecer un posicin ms natural de las manos sobre l. No puede negarse la utilidad y las ventajas que conlleva el empleo del ratn en la utilizacin de interfaces visuales, pero debemos prestar especial atencin a su diseo para prevenir la aparicin de problemas. Realice sta pequea prueba: extienda completamente durante unos segundos los dedos de la mano que utiliza para mover el ratn, a continuacin vuelva contraer los dedos hasta una posicin que le resulte cmoda y, sin moverlos, colquela sobre el ratn. A que no coinciden?. La postura con la que sujetamos el ratn est a medio camino entre una posicin natural, relajada, y la de agarre. Es por eso que suele provocar en determinadas personas dolores en los msculos extensores y flexores de los dedos, especialmente pulgar y meique. Por otra parte, el ahorro de costes provoca que se diseen y fabriquen ratones ambidextros, que al final no son totalmente adecuados ni para diestros ni para zurdos. Una alternativa a la utilizacin del ratn la encontramos en el trackball, con un diseo ms adaptado a la forma de la mano aunque en un principio puede resultar de ms difcil manejo.

Desde Gnome o KDE tambin disponemos de una herramienta

Revista Sntesis - Junio de 2006

Informtica y salud

Salud
Un reciente estudio realizado por la consultora Tickbox.net para el fabricante de hardware ViewSonic desvela que el treinta y nueve por ciento de los usuarios europeos de ordenadores que utiliza equipamiento obsoleto ven mermada su salud, productividad y motivacin. Lentos tiempos de respuesta, pantallas que pierden definicin y brillo por desajustes y desgaste de sus componentes, impresoras de tecnologa lser con alta emisin de ozono por carecer de los filtros adecuados o encontrarse en mal estado. Todos estos elementos juegan su papel en la merma de nuestra salud.

Figura 6 - Workrave nos aconseja la realizacin de una micropausa.

Utilizacin: el cmo
Ya tenemos los elementos adecuados para un perfecto puesto de trabajo, vamos ahora a utilizarlos de la mejor manera posible. Sentmonos en nuestra silla ergonmica y comprobemos su altura. sta debe ajustarse de manera que pueda apoyar los pies en el suelo, sin sentir presin sobre la cara posterior del muslo. Un ajuste demasiado bajo har que se eleven las rodillas y el peso caiga hacia la parte inferior de la espalda. Apoyando los antebrazos en los reposabrazos, las manos deben descansar sobre el teclado de manera que las muecas se encuentren en lnea con el brazo. Si stas se encuentra en ngulo notaremos cierta presin. (Figura 2)
Figura 7 - Pantalla de ajuste de preferencias de Workrave. La pantalla siempre debe estar frente al operador, para evitar continuos giros de cuello, y situarse de manera que la fuente de luz incida lateralmente sobre ella. Si detrs de la pantalla tenemos una ventana, el excesivo contraste entre la luz procedente del exterior y la emitida por sta no permitir su correcta visualizacin y obligar a los ojos a adaptarse continuamente a los cambios lumnicos ( Figura 3). Por el contrario, si la ventana se encuentra tras nosotros, la luz provocar molestos reflejos y disminuir su brillo relativo. Una excesiva iluminacin cenital tambin puede provocarnos deslumbramientos. Para permitir que la musculatura ocular trabaje de manera relajada el borde superior de la pantalla debe quedar unos grados por debajo de nuestra lnea visual. Si empleamos un atril, debe situarse junto a la pantalla y a la misma distancia visual que sta, para evitar continuas adaptaciones de enfoque. (Figura 4)

Una vez que tenemos el mobiliario y los equipos correctamente situados, comenzamos nuestro trabajo. An me sorprende observar a usuarios con aos de experiencia en el manejo de determinados programas hacer uso del ratn en acciones que pueden ser realizadas con facilidad mediante atajos de teclado. Separar una mano del teclado, girar la cabeza o los ojos, asir el ratn, volver a enfocar a la pantalla hasta localizar el puntero y pulsar el botn

Revista Sntesis - Junio de 2006

10

Informtica y salud

Salud
correspondiente para finalmente volver a situar la mano sobre el teclado... todo para colocar un texto en negrita, accin que podra haberse realizado con una simple pulsacin de tecla. Este mismo criterio debera estar presente cuando abordemos nuestros proyectos como programadores, ya que adems de favorecer la accesibilidad de los programas estaremos aportando nuestra pequea cuota a la prevencin de las lesiones por movimientos repetitivos (Figura 5).

...y el cunto
Figura 8 - Pantalla de descanso de Workrave con propuesta de ejercicios.

En efecto, como con el caf y el alcohol, conviene no pasarse. Despus de un da de trabajo, llegamos a casa y... de nuevo nos sentamos ante un

Figura 9 - Configuracin del refresco de pantalla en WindowsXP

Revista Sntesis - Junio de 2006

11

Informtica y salud

Salud

PC, por no hablar del cuantioso nmero de porttiles que han acompaado a su dueo al bao. Puede decir alguien que esto es sano? El tiempo medio de trabajo efectivo ante un ordenador no debera pasar de las cinco horas diarias y durante la jornada deben establecerse pausas peridicas para facilitar los movimientos de aquellos grupos musculares menos ejercitados. El principal tema de conversacin entre abogados os aseguro que no es el derecho, pero todos podemos adivinar cul es el tema preferido de los informticos. Procuremos, en fin, que las pausas sirvan para evadirnos durante unos minutos de lo que estamos haciendo, seguro que volvemos con nuevas ideas al trabajo.

Figura 10 - Configuracin del refresco de pantalla en Linux

Workrave
Cuando se est en plena efervescencia creativa, despus de largo rato de darle vueltas al diseo y tras haber cubierto unas cuantos cientos de lneas de cdigo y otras tantas por salir a quin se le ocurre levantarse y relajarse un poco? No estara mal que alguien nos avisara de la necesidad de tomar un descanso. As, para ayudarnos al establecimiento de pausas durante nuestro trabajo ante la pantalla, dos programadores, Rob Caelers y Raymond Penners han creado Workrave (http://www.workrave.com/), un programa que ayuda a prevenir y favorecer la curacin de las lesiones producidas por movimientos repetitivos (Figura 6). Aunque originalmente fue escrito para Linux, existe una versin para Windows, pudiendo integrarse como un aadido en la barra de tareas. Publicado bajo la licencia de fuentes abiertas GNU, diversos colaboradores lo han traducido a distintos idiomas, el castellano entre ellos. Monitorizando la actividad de teclado y ratn, avisa visual y acsticamente de la necesidad de realizar micropausas, descansos o, superado cierto lmite diario, impedir la utilizacin del ordenador. Altamente

Revista Sntesis - Junio de 2006

12

Informtica y salud

Salud

configurable, pueden establecerse mltiples temporizadores, posibilidad de posponer las pausas, fijar el nivel de impedimento al uso, y otras caractersticas (Figura 7). Quiz al principio, pueda resultar un tanto molesto que nos interrumpan en mitad del trabajo, pero os aseguro que se termina agradeciendo. Las micropausas, de treinta segundos por defecto, son idneas para descansar la vista. Durante las pausas, se propone la realizacin de una serie de ejercicios realmente beneficiosos, slo por eso, ya merece la pena instalarse el programa (Figura 8).

La vigilancia de la salud
Una frase que encontrar en cualquier texto sobre prevencin de riesgos laborales dice que la gestin de la salud de los trabajadores debe integrarse en la organizacin de la empresa, como un elemento ms. Y un aspecto de importancia dentro de la gestin es la vigilancia de la salud. El establecimiento de controles peridicos acordes con la actividad que se realiza es imprescindible para conocer y prevenir la aparicin de lesiones. Cualquier persona que desarrolle durante ms de cuatro horas diarias o veinte semanales trabajos con ordenadores debera someterse a un protocolo de vigilancia de la salud que incluya: Aparato visual:

Inspeccin del globo ocular y anejos. Examen de la refraccin y transparencia. Control del equilibrio muscular. Control de la integracin de la imagen Examen de los reflejos pupilares: fotomotor y de acomodacin. Funcionalidad de la musculatura ocular extrnseca en las coordenadas espaciales. Examen del sentido cromtico. A partir de los 40 aos de edad, examen de la tensin ocular y deteccin precoz de la presbicia.

Sistema osteomuscular

Examen de la columna vertebral y la pelvis Control y actualizacin sobre la esttica del raquis: Simetras/altimetras en la altura de hombros. Linealidad / desviacin de la columna (escoliosis y cifosis). Simetra en la altura de crestas iliacas (cadera).

Control y actualizacin de la movilidad de la extremidad superior, hombros, codos y muecas.

Un ltimo control, con cuestionarios especficos, debe valorar la carga y fatiga mental.

Revista Sntesis - Junio de 2006

13

Informtica y salud

Salud

Conclusiones
Empresarios, autnomos, asalariados, free-lance, estudiantes... la salud laboral y la prevencin de los riesgos asociados a las actividades profesionales es cosa de todos. Cada uno en su mbito de actuacin debe ser activo transmisor de una cultura de prevencin que, sin duda, nos beneficiar. A los empresarios preocupados por los costes asociados al establecimiento de una poltica preventiva en la empresa, decir que resulta ridculo si lo comparamos con la merma de ingresos debidos a la falta de productividad por el desarrollo del trabajo en un entorno inadecuado y las posibles bajas mdicas por motivos de salud provocados por stos o por una desacertada distribucin de las cargas de trabajo. Podra argumentarse la facilidad que en determinados pases existe para prescindir de los empleados y lo barato que pueden resultar las posibles indemnizaciones por daos. Pero en un entorno de tan alta especializacin, no es posible despreciar el coste marginal calculado en tiempo de formacin y experiencia. De igual manera, los incrementos de eficiencia y productividad asociados a un entorno de trabajo agradable han sido avalados por numerosos estudios. A los trabajadores, que compartan y hagan partcipes al resto de sus compaeros sobre las buenas prcticas y fomenten ante los gestores el conocimiento suficiente sobre algo tan importante, y que no se queda en la oficina cuando termina la jornada, como es la salud. En el siguiente apartado encontrar material suficiente para profundizar sobre el tema y, por supuesto, cualquier comentario o sugerencia ser bienvenida.

Bibliografa y direcciones de inters.


Instituto Nacional de Seguridad e Higiene en el Trabajo (INSHT). Nota Tcnica de Prevencin NTP 232: Pantallas de visualizacin de datos: fatiga postural. http://www.mtas.es/insht/ntp/ntp_232.htm INSHT. NTP 251: Pantallas de visualizacin: medida de distancias y ngulos visuales http://www.mtas.es/insht/ntp/ntp_251.htm INSHT. NTP 252: Pantallas de Visualizacin de Datos: condiciones de iluminacin http://www.mtas.es/insht/ntp/ntp_252.htm INSHT. Gua tcnica para la evaluacin y prevencin de los riesgos relativos a la utilizacin de equipos con Pantallas de visualizacin. http://www.mtas.es/Insht/practice/G_pantalla.htm RD. 488/1997. Disposiciones mnimas de seguridad y salud relativas al trabajo con equipos que incluyen pantallas de visualizacin http://www.mtas.es/Insht/legislation/RD/pantalla.htm Directiva de la Comunidad Econmica Europea 90/270/CEE http://europa.eu.int/smartapi/cgi/sga_doc?smartapi!celexapi!prod!CELEXnumdoc&lg=es&numdoc=31990L02 70&model=guichett Alfonso Calera y otros. La prevencin de riesgos en los lugares de trabajo http://www.istas.net/webistas/biblioteca.asp?idenlace=1458&seccion=6 Organizacin Internacional del Trabajo. La Salud y la seguridad en el trabajo. Ergonoma.

Revista Sntesis - Junio de 2006

14

Informtica y salud

http://training.itcilo.it/actrav_cdrom2/es/osh/ergo/ergoa.htm Salvador Moncada. Trabajo repetitivo y estrs. I Foro ISTAS de Salud Laboral. http://www.istas.net/sl/bajar/Iforo5.pdf Jos M Roel. Las patologas por movimientos y esfuerzos de repeticin: Informe para un dao anunciado. I Foro ISTAS de Salud Laboral http://www.istas.net/sl/bajar/Iforo6.pdf Revista Espaola de contactologa: Estabilidad lagrimal en usuarios de ordenador. http://www.oftalmo.com/sec/01-04-tomo-1/06.htm Ministerio de Sanidad y Consumo Protocolo de vigilancia sanitaria especfica en trabajadores de pantallas de visualizacin de datos. http://www.msc.es/ciudadanos/saludAmbLaboral/docs/datos.pdf Estudio de TickNet para ViewSonic. Nota de prensa. http://www.viewsoniceurope.com/UK/Press/2006/2006_05_Office_Environment_Research_EU.htm

La revista Sntesis es una publicacin gratuita, y quienes trabajan a diario en el Grupo Albor lo hacen desinteresadamente, sin cobrar nada a cambio. Por ello pedimos a nuestros suscriptores que no redistribuyan la revista o sus artculos por correo o en otras sedes. Si desea compartir la revista con un amigo, acrquele a la sede del Grupo Albor e invtele a que se suscriba a la revista y/o los foros, recuerde que son y sern gratuitos. De esa forma, Sntesis seguir creciendo y, con ella, la calidad de los artculos que la componen.

Revista Sntesis - Junio de 2006

15

Ricardo Pereryra - ricardo@grupoalbor.com

Internet

Creacin de un sitio web dinmico I


En esta ocasin veremos como preparar todo el software necesario, y realizaremos una pequea pgina de prueba.
Hasta hace un tiempo la gran mayora de sitios web eran estticos. Lgicamente el avance de Internet, su proliferacin en todo el mundo, y la creciente necesidad de las organizaciones de figurar en el mundo virtual han hecho que la calidad y prestaciones de la www avancen considerablemente. Hoy en da prcticamente se puede decir que un sitio web debe ser dinmico para ser un sitio web completo. Pero vamos por partes. A que me refiero con un sitio web dinmico? En Internet encontraremos dos tipos de sitios claramente diferenciados. El primero de ellos, el tradicional hace varios aos atrs, consiste en un grupo de documentos de hipertexto1 con grficos, fotos y animaciones. Estos documentos son creados en forma directa por el administrador del sitio web, mediante algn editor de etiquetas HTML. Crear un sitio web esttico puede ser tan sencillo como escribir un documento de texto, ya que los modernos editores facilitan la tarea al punto de hacer innecesario el conocimiento de cdigo HTML. Crear un sitio web esttico es sencillo por naturaleza, pero el mantenimiento del mismo puede ser un verdadero dolor de cabeza. Los sitios web dinmicos, en cambio, son generados en forma automtica mediante algn mecanismo que el diseador haya programado. Este mecanismo puede ser muy variado, desde las CGI2 hasta los modernos lenguajes de servidor. A diferencia de los sitios estticos la programacin de un sitio dinmico puede ser muy costosa, pero las tareas de mantenimiento, personalizacin y reutilizacin se ven enormemente facilitadas. Normalmente se utilizan bases de datos para almacenar informacin de algn tipo, realizar consultas sobre los productos que la empresa ofrece y, lgicamente, realizar compras on-line. Estos sitios web tambin estn escritos con cdigo HTML, aunque a veces se utiliza nicamente algunas aplicaciones, como FLASH, que permiten hacer sitios completos. La realidad es que de cara al usuario que accede a una pgina todas ellas son estticas, ya que finalmente lo que muestran son un conjunto de etiquetas HTML, hojas de estilo CSS, algo de Javascript y alguna que otra animacin Flash. El dinamismo reside del lado del servidor, en el que las pginas son construidas al vuelo, a peticin y no existen fsicamente en el disco.

Comencemos por el principio


Para que nadie quede fuera de esta pequea gua, vamos a explicar, aunque no con demasiado detalle, prcticamente todos los conceptos necesarios. El primero de ellos, el ms necesario: HTML.
1 Hipertexto significa bsicamente texto con enlaces. La posibilidad de saltar de un documento a otro, con tan solo hacer clic sobre una palabra. 2 CGI significa Common Gateway Interface. Un mecanismo mediante el cual el diseador puede crear ejecutables que procesarn determinados pedidos y generarn el cdigo html correspondiente. Uno de los primeros mtodos para crear pginas web dinmicas.

Revista Sntesis - Junio de 2006

16

Creacin de un sitio web dinmico

Internet

Qu es HTML?
HTML (Hyper Text Markup Lenguaje) es un lenguaje simple de definicin de documentos, que nos permite crear pginas web para ser visualizadas posteriormente en un navegador. Lgicamente, la amplia variedad de navegadores web hacen que un mismo documento a veces se visualice en forma diferente, esto es as porque no todos los navegadores disponen de las mismas capacidades, y an tenindolas, las implementaciones son diferentes.

Figura 1

El lenguaje HTML se basa en etiquetas, por ejemplo para subrayar un bloque de texto el mismo debe demarcarse mediante <u> y </u>. Para ponerlo en negrita se utilizan las etiquetas <b> y </b>. El cdigo HTML contenido en una pgina web debe estar delimitado por las etiquetas <html> y </html>: Estas son indicadoras del inicio y fin del documento. Dentro de las mismas el documento se halla dividido en dos grandes secciones: El encabezado y el cuerpo. El encabezado de un documento HTML se delimita con las etiquetas <head> y </head>. Sirve para incluir directivas que contengan informacin adicional al contenido, por ejemplo el ttulo de la pgina, el programa utilizado para la creacin, o una serie de palabras clave para que los buscadores clasifiquen correctamente nuestro sitio. El cuerpo del documento debe estar delimitado por las etiquetas <body> y </body>. Es posible utilizar modificadores de esta etiqueta para indicar el tipo y color de fuente por defecto, el color o imagen de fondo, la alineacin del texto, etc.
<html> <head> <title>Pagina de ejemplo</title> </head> <body bgcolor="#000000" topmargin="10"> <b>Este texto saldr en negrita</b> <i>Este texto saldr en cursiva</i> <b><i>Este texto saldr en negrita y cursiva</i></b> </body> </html>

Existen muchas etiquetas HTML, y cada una posee gran cantidad de modificadores, los cuales no vamos a ver aqu Quien quiera aprender ms sobre HTML puede visitar algunos de los tantos tutoriales on-line que encontrar en la web. Incluyo enlaces a algunos de ellos: Tutorial en espaol: http://html.conclase.net/tutorial/html/ Tutorial en espaol: http://www.webestilo.com/html/ Referencia del HTML en ingls: http://www.w3.org/MarkUp/

Revista Sntesis - Junio de 2006

17

Creacin de un sitio web dinmico

Internet

El servidor web
Para que un sitio web pueda funcionar es necesario que una computadora (servidor web) responda a los pedidos que realizan los usuarios a travs de su navegador. Qu sucede cuando escribimos www.grupoalbor.com en nuestro navegador? De dnde sale la informacin? En dnde est alojada esa informacin? Lo que sucede es que el navegador realiza una consulta, para que el sitio www.grupoalbor.com responda, y luego, una vez que se estableci el contacto, le pide que le transfiera la pgina solicitada. El sito www.grupoalbor.com es, precisamente, una computadora que est preparada y configurada para escuchar y esperar los pedidos de informacin sobre ese sitio. Y cuando alguien lo solicite, interactuar con el navegador, enviando la informacin que sea necesaria.

Figura 2

Es posible crear un documento HTML y simplemente abrirlo en el navegador. Este documento sera esttico y probablemente se visualice sin problemas. Si en cambio pretendemos crear documentos dinmicos es necesario disponer de un mecanismo mediante el cual se generen el archivo automticamente. Ya sea que utilicemos CGI, PHP, ASP, u otro similar, ser necesario definir antes un servidor web. De hecho, siempre que se realice un desarrollo basado en web es requisito mnimo e indispensable tener un servidor de prueba, en el cual podremos probar que nuestro cdigo funciona como se pretende. Existen muchas aplicaciones que realizarn la tarea de convertir nuestra pc en un servidor web. Algunas de ellas, quizs las ms populares3 son: - Internet Information Server (IIS): Es el servidor web integrado que poseen los sistemas Operativos de Microsoft en sus productos Windows NT, Windows XP, Windows 2000 y Windows 2003. Para poder utilizarlo en toda su plenitud y sin restricciones es necesario tener la versin server del sistema operativo. - Apache: El servidor web tradicional de la plataforma Linux. Su amplia difusin, y sus caractersticas lo han hecho trascender fronteras, y actualmente podemos encontrar una versin hecha especialmente para entornos w32.

3 Vale la pena aclarar que estamos hablando de la plataforma Windows. Revista Sntesis - Junio de 2006 18

Creacin de un sitio web dinmico

Internet

- Personal Web Server (PWS): Es el servidor web disponible como un adicional para Windows 98. Es un programa pequeo, de fcil instalacin y configuracin, pero recomendado solamente como servidor de pruebas.

Flash
Flash es una aplicacin muy potente de Macromedia, que naci con la idea de crear animaciones especialmente diseadas para ser vistas por la web. Lgicamente el requisito fundamental de una animacin de este tipo es rapidez en la transferencia (y por lo tanto visualizacin), y para ello Macromedia decidi instalar un pequeo intrprete en el navegador, de manera de que el servidor solo transfiera un pequeo archivo con cdigo, el cual ser interpretado en el la mquina del visitante, mostrando el resultado sobre la ventana del navegador. Posteriormente, y debido a su xito, incorpor la posibilidad de incluir cdigo (denominado ActionScript) que permite realizar archivos an ms pequeos, y brindarle a las animaciones una dosis de dinamismo muy importante. Hoy en da encontraremos sitios completos e incluso algunos juegos muy interesantes realizados exclusivamente con Flash.

VBScript y JavaScript
En la medida en que la utilizacin de Internet fue proliferando, las capacidades que ofrece el HTML se fueron quedando cortas. Entonces aparecieron los lenguajes de script, que son pequeos bloques de cdigo insertos entre el cdigo HTML que el navegador interpreta, y ejecuta. Estos pequeos lenguajes tienen limitaciones, lgicamente, pero permiten realizar diversos clculos, interactuar con el usuario, y realizar algunos efectos la verdad sorprendentes. El defecto de los lenguajes de script es, precisamente, que se ejecutan en la mquina del visitante, y tambin dependen del navegador que se utilice. Es por ello que no son del todo confiables, y las funciones que se pueden utilizar son solo las estndar. Este pequeo inconveniente hizo que aparezcan sitios webs que se visualizan correctamente solo con Internet Explorer, otros con NetScape, etc.

ASP, JSP y PHP


Para solucionar los problemas de los lenguajes de script nacieron los lenguajes de servidor. ASP es la versin de servidor de VBScript, y JSP de JavaScript. Estos lenguajes funcionan de forma similar, ya que se insertan trozos de cdigo entre el HTML, pero la diferencia radica en que es el servidor quin los analiza antes de enviar la pgina: los ejecuta, y con el resultado de este proceso construye la pgina final, la que ser enviada al navegador. Lgicamente, para poder utilizar estas tecnologas es necesario que el servidor (IIS, Apache o PWS) estn capacitados para interpretar y ejecutar cdigo (ASP, JSP o PHP). Muchas veces esto significa instalar el agregado necesario. Como contra, los lenguajes de servidor no pueden realizar tareas al instante (en el mismo navegador), como validar los datos cargados en un formulario antes de enviarlo. Habitualmente se utiliza una combinacin de lenguajes de script y de servidor.

Revista Sntesis - Junio de 2006

19

Creacin de un sitio web dinmico

Internet

MySQL y phpMyAdmin
MySQL es un servidor de bases de datos creado especficamente para la web. Al igual que Apache est ntimamente relacionado con la plataforma Linux aunque en la actualidad existen versiones especficas para win32. Histricamente MySQL siempre fue limitado en sus capacidades, al punto de no disponer soporte para procedimientos almacenados, o integridad referencial. Cuestiones no poco importantes, pero superfluas si se considera que el mbito de utilizacin estaba circunscrito a la web. El avance de Internet y la complejidad de las aplicaciones que las empresas necesitan desarrollar sobre la web permitieron la evolucin de MySQL, aunque todava no representa una alternativa a motores de base de datos como MS-SQL, Postgress, Oracle o Informix. phpMyAdmin es una excelente utilidad que nos permite administrar desde el navegador las bases de datos de nuestra instalacin de MySQL. Trabaja desde siempre con PHP, Apache y lgicamente MySQL, as que es preciso tenerlos instalados y configurados para ponerla en funcionamiento. Debido a la gran utilidad que phpMyAdmin nos ofrece es habitual encontrarla instalada en la mayora de los servidores que utlizan MySQL como herramienta bsica de administracin.

El software necesario
Para crear nuestro sitio web vamos a instalar un servidor Apache en una mquina con Windows XP Professional, instalaremos adicionalmente PHP, MySQL y phpMyAdmin, luego configuraremos todo, y recin estaremos en condiciones de escribir nuestro primer script de prueba. En una entrega posterior crearemos una pequea aplicacin hecha en Delphi con la que actualizaremos la base de datos, y nuestro sitio web se actualizar automticamente para mostrarnos el resultado buscado. El primer paso entonces, es conseguir el soft necesario.

Apache
Para instalar Apache Server debemos entrar a la pgina oficial y realizar la descarga. La direccin es: The Apache Software Foundation - http://www.apache.org/ La pgina de descargas, ya especficamente situados sobre un mirror, es: Descargas Binarios Win32 - http://httpd.apache.org/download.cgi En la pgina anterior encontraremos toda la informacin necesaria para realizar la descarga correcta, pero si nos complicamos mucho, recomiendo bajar solo el archivo MSI: Apache Server v2.0.55 MSI - http://apache.mesi.com.ar/httpd/binaries/win32/apache_2.0.55-win32-x86no_ssl.msi Lgicamente la versin cambia constantemente, no se sorprendan si este ltimo enlace no funciona, ser porque apareci una nueva versin del programa.

Revista Sntesis - Junio de 2006

20

Creacin de un sitio web dinmico

Internet

PHP
Para instalar el soporte PHP debemos entrar al sitio web de PHP. La direccin es: PHP Hypertext Preprocessor - http://www.php.net/ La pgina de descargas es: http://www.php.net/downloads.php En esta pgina descargaremos la ltima versin estable de PHP, en este caso la 5.1.2 Encontraremos tres tipos de descarga, y elegiremos la versin ZIP, no la que dice Installer ya que viene solo con soporte CGI, y no trae las libreras para utilizar MySQL. Si nos liamos de nuevo, podemos utilizar el siguiente enlace, sin garantas: PHP 5.0.3 Select mirror - http://ar2.php.net/get/php-5.1.2-Win32.zip/from/a/mirror En este caso solo restar seleccionar el mirror deseado, y descargar el archivo. Y repito: Lgicamente la versin cambia constantemente, no se sorprendan si este ltimo enlace no funciona, ser porque apareci una nueva versin del programa.

MySQL
La pgina principal de MySQL, a donde podemos conseguir toda la informacin sobre el producto, es: MySQL: The Worlds Most Popular Open Source Database - http://dev.mysql.com/ La pgina de descargas es: MySQL AB :: MySQL 5.0 Downloads - http://dev.mysql.com/downloads/mysql/5.0.html Encontraremos all muchos tipos de descargas, y tres especficas para entornos win32. En la siguiente direccin encontraremos una explicacin de cada uno de estos paquetes: MySQL 5.0 Reference Manual :: 2.3.1 Choosing http://dev.mysql.com/doc/refman/5.0/en/windows-choosing-package.html an installation package -

Y, finalmente, si no queremos leer, y no nos interesan todas esas cosas que tienen para comentarnos (o advertirnos) la gente de MySQL, podemos usar este triste enlace: http://dev.mysql.com/get/Downloads/MySQL-5.0/mysql-essential-5.0.19-win32.msi/from/pick phpMyAdmin La pgina principal del proyecto phpMyAdmin la encontraremos en: phpMyAdmin MySQL Database Administration Tool - http://www.phpmyadmin.net/home_page/index.php La zona de descargas la encontraremos en la siguiente URL: phpMyAdmin -> Downloads MySQL http://www.phpmyadmin.net/home_page/downloads.php Database Administration Tool -

En ella encontraremos disponibles para descarga varias versiones de phpMyAdmin. Lo recomendable es bajar las ms actualizada, en este momento la 2.8.0.3. Como podrn apreciar se ofrecen tres tipos de descarga, lo ms sencillo es escoger la versin ZIP. http://prdownloads.sourceforge.net/phpmyadmin/phpMyAdmin-2.8.0.3.zip?download

Revista Sntesis - Junio de 2006

21

Creacin de un sitio web dinmico

Internet

Como en todas las ocasiones anteriores, es posible que este enlace caduque con la simple aparicin de una nueva versin.

Figura 3

Revista Sntesis - Junio de 2006

22

Creacin de un sitio web dinmico

Internet

Instalacin y configuracin de los programas


Para la realizacin del ejemplo se contempla la instalacin de cada uno de estos programas del modo ms sencillo posible. A lo largo de este procedimiento es de esperar que se encuentren diversas alternativas. No precisamente debe elegirse la opcin detallada en el artculo, aunque es recomendable hacerlo slo si se sabe exactamente qu se est haciendo. Especificar cada una de las opciones de instalacin, o las diferencias originadas por un cambio de plataforma (Windows XP, Windows 98, Linux, etc.), escapan de los objetivos de este artculo, aunque los animo a intentarlo: Todo el conocimiento necesario se encuentra en los documentos que acompaan al software descargado, o en el peor de los casos, en la web de cada uno de ellos. Dicho esto lo primero que haremos es instalar Apache Server. La instalacin se iniciar si hacemos doble clic sobre el archivo MSI que descargamos, y su complejidad es prcticamente nula. Se instala como cualquier otro programa que hayamos instalado en nuestro Windows XP. El asistente de instalacin nos realizar un par de preguntas necesarias para la configuracin inicial del server, dejemos las opciones por defecto y presionemos Next ante una eventual duda. El segundo paso ser instalar PHP. Para ellos simplemente hay que descomprimir el archivo zip que bajamos de Internet en la carpeta C:\PHP4. Posteriormente tendremos que modificar algunos valores en los archivos de configuracin, pero esos pasos sern descriptos en el siguiente apartado: la configuracin. A continuacin instalaremos phpMyAdmin. Ser tan sencillo y parecido como con PHP. Pero antes de ellos crearemos una carpeta en la raz de nuestro disco rgido: c:\www. Dentro de ella descomprimiremos el ZIP que descargamos desde la web del Proyecto phpMyAdmin y encontraremos que nos crea una carpeta llamada phpMyAdmin-2.8.0.3, para finalizar la renombraremos a phpmyadmin5. Dentro de ella debern estar los archivos Figura 4 de la aplicacin, citando uno de ellos: c:\www\phpmyadmin\index.php. En el apartado siguiente crearemos un archivo de configuracin para esta sorprendente utilidad. Finalmente instalaremos MySQL. Haremos doble clic sobre el archivo que descargamos para iniciar la instalacin. Elegiremos la instalacin tpica, y cuando nos pida loguearnos en la web de MySQL simplemente haremos clic en el botn skip (saltar)6. Pero la instalacin de MySQL no termina all. Al finalizar la misma aparecer un asistente que sirve para crear un archivo de configuracin adecuado, configurar un servicio de Windows, y establecer el password para la cuenta principal (root).
4 En realidad puede utilizarse cualquier otro nombre de directorio. Para facilitar el seguimiento del ejemplo recomiendo utilizar el nombre C:\PHP. 5 Al igual que en la instalacin de PHP el nombre utilizado es indiferente, aunque por simplicidad es recomendable cambiarlo a algo ms sencillo que phpMyAdmin-2.8.0.3. 6 Si el lector desea registrar su copia de MySQL lo animo a hacerlo. Este proceso no influir de ninguna manera en la continuidad del ejemplo.

Revista Sntesis - Junio de 2006

23

Creacin de un sitio web dinmico

Internet

Como habamos dicho, en la ltima ventana de instalacin aparecer una opcin que dice Configure the MySQL Server now, tildaremos esa opcin y presionaremos el botn Finish. A continuacin se iniciar el asistente. La primer ventana del asistente solo contiene informacin, haremos clic en Next. La segunda ventana nos permite elegir el tipo de configuracin: Detallada o estndar. Seleccionaremos Detailed configuration y presionaremos Next. La tercer ventana nos permite elegir el tipo de servidor: Mquina de desarrollo, Servidor, o Servidor dedicado de MySQL. Cada uno de estos equipos utiliza diferentes cantidades de memoria del sistema, por lo tanto elegiremos Developer machine, de manera que el servidor MySQL utilice una mnima cantidad de memoria. A continuacin configuraremos el tipo de base de datos: Multifuncional, Solo Transaccional, o Solo No-Transaccional. Elegiremos Multifunctional Database y presionaremos Next. La siguiente ventana nos permite cambiar la ubicacin del archivo InnoDB. Dejaremos esta opcin como aparece, y presionaremos Next. En el siguiente paso el asistente nos permitir seleccionar la cantidad aproximada de conexiones concurrentes al servidor: Sistema de Soporte de Decisiones (20 accesos concurrentes), Sistema de Procesamiento Transaccional Online (500 accesos concurrentes), o Configuracin Manual (ingresaremos la cantidad de accesos concurrentes). Dejaremos seleccionada la opcin Decisin Support (DDS) / OLAP y presionaremos Next. A continuacin habilitaremos el acceso al servidor MySQL a travs de TCP/IP, y seleccionaremos el nmero de puerto. Dejaremos tildada la opcin Enable TCP/IP Networking y el puerto por defecto (3306). Presionaremos Next. En la misma ventana encontraremos la opcin para establecer el modo del servidor SQL. Dejaremos tildada la opcin por defecto Activar el modo estricto que fuerza a l servidor a comportarse como un servidor de base de datos tradicional. En el siguiente paso seleccionaremos el juego de caracteres por defecto, dejaremos la opcin que aparece tildada: Estndar Character Set y presionaremos Next. La siguiente ventana nos permitir hacer dos cosas, instalar el servidor MySQL como un servicio, y ejecutarlo automticamente o no, y agregar el directorio de binarios (donde residen los ejecutables de MySQL) en la variable PATH de Windows, de manera de ejecutarlos desde la lnea de comando. Dejaremos los valores por defecto Install as Windows Service con el nombre MySQL, la opcin Launch the MySQL Server Automatically, y adicionalmente tildaremos la opcin Incluye Bin directory in Windows PATH. Presionaremos Next. A continuacin dejaremos tildada la opcin Modify Security Settings, ingresaremos un password para la cuenta principal (root), y por motivos de seguridad no tildaremos la opcin Enable root access from remote machines. Dejaremos sin tilde la opcin Create An Anonymous Account y presionaremos Next. Finalmente, la ltima ventana nos informar los pasos que ejecutar el asistente para aplicar la configuracin que seleccionamos, solo presionaremos Execute y esperaremos el resultado. Una vez aplicada la configuracin, solo restar presionar el botn Finish para salir del asistente. Al terminar de instalar todos los programas, a pesar que ninguno de ellos ha pedido reiniciar, como buena costumbre adquirida como usuario de Windows, reiniciaremos la mquina antes de ejecutar alguno de ellos.

Revista Sntesis - Junio de 2006

24

Creacin de un sitio web dinmico

Internet

Configuracin de los programas


El paso siguiente es configurar los tres programas instalados. Es preciso modificar algunos valores de configuracin ya que ellos trabajarn en forma conjunta: Cuando un visitante ingrese a nuestra web, el servidor Apache recibir el pedido y servir la pgina. Como nuestro archivo HTML contendr scripts PHP, Apache necesitar procesarlos y por ello deber estar configurado para utilizar los servicios PHP. Y como a travs de cdigo PHP nuestra pgina visualizar frecuentemente datos de una base de datos MySQL, es preciso que PHP cuente con las libreras necesarias para poder hacerlo, y que MySQL se encuentre correctamente instalado y configurado.

Apache Server
En primer lugar ejecutaremos un script que la instalacin nos ha dejado en: Inicio Todos los programas Apache HTTP Server 2.0.55 Configure Apache Server Test Configuration. Lo que este script hace es comprobar si el archivo principal de configuracin contiene errores. Si la ventana se abre y se cierra sin mostrar mensajes de error, est todo en orden. De haber un error la ventana no se cerrar automticamente y podremos visualizar una pequea descripcin del error, suficiente para ubicar la lnea en el archivo de configuracin y revisarla. El archivo de configuracin se llama httpd.conf y podemos acceder a l fcilmente desde Inicio - Todos los programas Apache HTTP Server 2.0.55 Configure Apache Server - Edit the Apache httpd.conf Configuration File (Figura 1). Ms adelante en esta gua modificaremos algunos valores en este archivo. Esta versin de Apache que hemos descargado instala el servidor web como un servicio de Windows. Para controlar este servicio y no liarnos tenemos una consola llamada Apache Service Monitor desde la cual podremos detener, pausar, o reiniciar nuestro servidor Apache. Como podrn observar en la ventana del Service Monitor (Figura 2), la instalacin por defecto de Apache ha creado un servicio llamado Apache2, que dada la luz verde del led indicador, se encuentra iniciado. Con los botones a la derecha, en la ventana, podremos manejar dicho servicio. Para comprobar el correcto funcionamiento de nuestro servidor web abriremos un navegador, Internet Explorer por ejemplo, y en la barra de direcciones escribiremos http://localhost y presionaremos Enter. Si todo anduvo bien (y as debera ser) el servidor Apache recibir el pedido, y servir una pgina por defecto similar a la de la figura 3. Es importante destacar que a travs de esta pgina es posible navegar por la documentacin incorporada en la instalacin de Apache Server, con la que seguramente podremos sacarnos varias dudas, y aprender sobre aspectos no considerados en esta gua. Ahora editaremos el archivo de configuracin de Apache. El mismo se llama httpd.conf, y est ubicado en C:\ARCHIVOS DE PROGRAMA\APACHE GROUP\APACHE2\CONF, y tenemos un enlace a l denominado Edit the Apache httpd.conf Configuration File, en Inicio Todos los programas Apache http Server 2.xx Configure Apache Server, tal como podemos ver en la imagen nmero 1 El men de Apache. Si Windows nos pregunta con qu programa abrir el archivo con extensin .conf simplemente elegiremos el bloc de notas (notepad.exe) y continuaremos. Al inicio del archivo httpd.conf podremos leer un texto ms que elocuente:
***************************************************************** # Do NOT simply read the instructions in here without understanding # what they do. They're here only as hints or reminders. If you are unsure # consult the online docs. You have been warned. *****************************************************************

Revista Sntesis - Junio de 2006

25

Creacin de un sitio web dinmico

Internet

Textualmente dice: No leas simplemente estas instrucciones sin entender que es lo que ellas hacen. Ellas estn aqu solo como consejos o recordatorios. Si no ests seguro consulta la documentacin online. Ests advertido Las instrucciones que aqu encontrars para configurar apache son a modo ilustrativo, con fines educativos y bajo ningn concepto te bases en ellas para crear un servidor de produccin. La instalacin de Apache requiere considerar muchos factores relacionados con la seguridad y estabilidad del servidor, y es preciso que leas y entiendas los documentos que Apache Group ha puesto en la web para ello. Ests advertido. En primer lugar estableceremos el lugar en donde residirn los archivos del sitio, a ser servidos por el servidor Apache. Para ello buscaremos la lnea que dice:
DocumentRoot "C:/Archivos de Programa/Apache Group/Apache2/htdocs"

Y la reemplazaremos por:
DocumentRoot C:/www (Notar que no se usa la barra invertida)

Si lo deseas puedes dejar el directorio anterior, o usar uno con cualquier nombre. Yo uso C:\www porque su nombre es bastante explicativo, y queda sobre la raiz del disco, facilitando su acceso. Como hemos modificado la carpeta raz de donde se sirven los archivos, es preciso modificar tambin la seccin del archivo de configuracin en donde se configura dicho directorio. Para ello buscaremos las siguientes lneas:
# # This should be changed to whatever you set DocumentRoot to. # <Directory "C:/Archivos de Programa/Apache Group/Apache2/htdocs">

Y obedeciendo al consejo de la gente de Apache Group, la reemplazaremos por:


# # This should be changed to whatever you set DocumentRoot to. # <Directory "C:/www">

En las lneas siguientes podremos apreciar algunos parmetros que se incluyen en la configuracin del directorio, entre las etiquetas <Directory XX> y </Directory>:
<Directory "C:/www"> Options Indexes FollowSymLinks AllowOverride None Order allow,deny Allow from all </Directory>

No modificaremos estos parmetros, ya que son los recomendados por la configuracin de Apache. De todos modos en la documentacin de Apache (incluida en la instalacin) y la web de Apache Group podremos encontrar informacin sobre estas sentencias. Ahora le diremos al servidor cuales son los nombres de archivo por defecto, para cuando no se explicite en la URL. Por ejemplo, si la URL es http://www.grupoalbor.com, le podemos decir al servidor que busque, en esa carpeta el archivo index.html, o cualquier otro. De manera que el servidor devolvera en realidad htttp://www.grupoalbor.com/index.html. Buscaremos la lnea que dice:

Revista Sntesis - Junio de 2006

26

Creacin de un sitio web dinmico

Internet

DirectoryIndex index.html index.html.var

Y la reemplazaremos por:
DirectoryIndex index.php index html index.html.var

De esa forma, cuando se en la URL no se explicite el archive pedido, sino solo la carpeta, el servidor buscar los archivos especificados, en el orden especificado: 1. index.php 2. index.html 3. index.html.var Para probar que nuestro servidor funcione, crearemos la carpeta C:\www y crearemos un archivo en el. Lo llamaremos index.html y dentro escribiremos el siguiente texto:
<html> <head></head> <body> Este es mi servidor Apache desde www. </body> </html>

Sin olvidarnos de guardar los cambios en el archivo httpd.conf reiniciaremos el servicio Apache2, abriremos el explorador y escribiremos en la barra de direcciones http://localhost y presionaremos Enter. Si todo anda bien (y as debera ser) aparecer en nuestro navegador Este es mi servidor Apache desde www (Figura 4).

PHP
Como primer paso, una aclaracin. En teora los archivos que descomprimimos fueron ubicados en la carpeta C:\PHP. En realidad podemos utilizar cualquier otro nombre, siempre teniendo cuidado que el mismo no contenga espacios en blanco, ya que algunos servidores web pueden fallar por este motivo.

Figura 5

Como segundo paso agregaremos el directorio C:\PHP en el PATH de Windows, ya que algunos archivos, como php5ts.dll deben estar disponibles, y poder encontrarse fcilmente para que funcionen los mdulos de servidor, el ejecutable CGI, y el CLI (para la lnea de comandos). Para ello debemos ir al Panel de Control de Windows XP, y entrar al cono System. Abrimos la pestaa Advanced y haremos clic sobre el botn Environment Variables. Luego en la seccin System variables buscamos la variable llamada PATH y haremos clic sobre el botn Edit (Ver figura 5). Se nos abrir una ventana en donde podremos modificar el path, agregaremos la siguiente cadena ;C:\PHP (notar que los directorios se separan con ;) El tercer paso es configurar el servidor web para que encuentre el archivo de configuracin de Php. Para ello editaremos nuevamente el archivo httpd.conf, de la instalacin de Apache. Buscaremos el fin de la seccin 1 (Global Environment) y agregaremos las siguientes lneas:

Revista Sntesis - Junio de 2006

27

Creacin de un sitio web dinmico

Internet

# Ubicacin del archivo INI de PHP PHPIniDir "C:/php"

Y adicionalmente agregaremos dos lneas, en la seccin correspondiente. Primero buscaremos la seccin Dynamic Shared Object (DSO) Support y al final de ella agregaremos:
# Agregado para configurar PHP5 LoadModule php5_module "c:/php/php5apache2.dll"

Luego buscaremos la seccin AddType y agregaremos en ella:


# Agregado para configurar PHP5 AddType application/x-httpd-php .php

Para finalizar guardaremos los cambios realizados en el archivo httpd.conf. Ahora configuraremos el archivo PHP.INI. Si entramos a la carpeta C:\PHP podremos notar que no existe dicho archivo, en cambio encontraremos dos archivos llamados php.ini-dist y php.ini-recommended (recomendado por razones de seguridad y performance). Lo que haremos ser crear una copia del archivo php.ini-recommended y renombrarla php.ini, y asegurarnos que resida en el directorio C:\PHP. A continuacin editaremos dicho archivo. En primer lugar buscaremos la lnea que dice:
doc_root =

Y la reemplazaremos por:
doc_root = C:\www

La segunda modificacin que realizaremos ser para cambiar el directorio en donde PHP buscar las extensiones. Buscaremos la lnea que dice:
extension_dir = "./"

y la reemplazaremos por:
extension_dir = "c:/php/ext/"

Y hasta aqu funciona?


Exacto. Como podemos verificar que hasta aqu, lo hecho funciona? Hemos instalado, configurado y probado que el servidor Apache funciona. Ahora hemos instalado y configurado PHP para funcionar con Apache, as que lo probaremos. En la carpeta que habamos creado (c:\wwwroot) buscaremos el archivo que habamos creado index.html y lo renombraremos por index.php. Lo abriremos para edicin (con el block de notas) y adentro escribiremos:
<?php phpinfo() ?>

Solo eso, borrando cualquier otra cosa que haya contenido. Entonces, sin olvidarnos de guardar los cambios hechos en los archivos httpd.conf y php.ini, reiniciaremos el servidor Apache, y abriremos el explorador. Al igual que en la primer prueba escribiremos en la barra de direcciones http://localhost y presionaremos Enter.

Revista Sntesis - Junio de 2006

28

Creacin de un sitio web dinmico

Internet

Si todo sale bien saldr una pgina de informacin de PHP similiar a la de la figura 6, en donde podemos ver el estado actual de la configuracin. No hace falta entender que dice esa pgina, con solo verla basta para saber que Apache carg correctamente los mdulos de PHP. Ahora, configuraremos PHP para que acceda a la base MySQL.

Figura 6

MySQL
En primer lugar editaremos el archivo php.ini para indicarle a PHP que utilice la extensin de MySQL. Buscaremos en dicho fichero la lnea que dice:
;extension=php_mysql.dll

Y la sacaremos el ; que tiene al inicio (la lnea est comentada), para que quede:
extension=php_mysql.dll

Revista Sntesis - Junio de 2006

29

Creacin de un sitio web dinmico

Internet

Para probar que PHP puede efectivamente acceder a MySQL, crearemos una pequea pgina de ejemplo. Buscaremos el archivo index.php que habamos creado inicialmente y cambiaremos su contenido por:
<html> <head></head> <body> <p>Vamos a probar mySQL...</p> <?php $link = mysql_connect('localhost', 'root', '******'); if (!$link) { die('Could not connect: ' . mysql_error()); } echo 'Connected successfully'; mysql_close($link); ?> <p>Fin del script</p> </body> </html>

Importante es destacar que en la lnea:


$link = mysql_connect('localhost', 'root', '******');

Debe reemplazarse ****** por la contrasea que hayan ingresado al momento de instalar MySQL. Si finalmente aparece (y debera ser as) el siguiente resultado en su navegador al abrir http://localhost, quiere decir que todo est correctamente configurado y en funcionamiento (Figura 7). Si por algn motivo el resultado no es el esperado, a no desesperarse. Es normal en ambientes Windows tener que reiniciar la Pc antes de tirar todo por la borda. Desconozco puntualmente en cual caso, y en cual no, es necesario reiniciar la Pc en vez de slo reiniciar el servicio Apache2, pero a modo ilustrativo yo tuve que reiniciar mi Windows para que la prueba de MySQL fuera satisfactoria.

Figura 7

Ahora configuramos la utilidad para manejar la BD


Ha llegado el momento de configurar y probar phpMyAdmin. el primer paso ser copiar el archivo c:\www\phpmyadmin\libraries\config.default.php a la carpeta principal de phpMyAdmin: c:\www.phpmyadmin. Lo renombraremos por config.inc.php de manera que quede: c:\www\phpmyadmin\config.inc.php.

Revista Sntesis - Junio de 2006

30

Creacin de un sitio web dinmico

Internet

Ahora abriremos el mencionado archivo, pero no lo haremos con Notepad sino con Wordpad, ya que el formato del mismo nos complicar la edicin en el Bloc de Notas de Windows. En primer lugar indicaremos el lugar en donde residen los archivos del aplicativo. Para ello buscaremos la siguiente lnea:
$cfg['PmaAbsoluteUri'] = '';

Y la cambiaremos por:
$cfg['PmaAbsoluteUri'] = 'http://localhost/phpmyadmin';

Ahora estableceremos los valores correspondientes para el servidor de base de datos MySQL, el nombre de usuario para conectarnos con ella, y el password. Buscaremos las siguientes lneas:
$cfg['Servers'][$i]['host'] = ''; $cfg['Servers'][$i]['controluser'] = ''; $cfg['Servers'][$i]['controlpass'] = '';

Y las reemplazaremos por:


$cfg['Servers'][$i]['host'] = 'localhost'; $cfg['Servers'][$i]['controluser'] = 'root'; $cfg['Servers'][$i]['controlpass'] = '*******';

Siendo ***** el password que ingresamos al momento de instalar MySQL.

Figura 8

Revista Sntesis - Junio de 2006

31

Creacin de un sitio web dinmico

Internet

Para finalizar guardaremos los cambios y probaremos la instalacin abriendo en el navegador la siguiente URL: http://localhost/phpmyadmin. Si todo sale bien aparecer una ventana similar a la de la figura 8. En donde podremos apreciar, si nos desplazamos hasta la parte inferior de la misma, la advertencia que vemos en la figura 9. Claramente no estamos cargando una extensin php para soportar la tipografa multibyte que phpMyAdmin utiliza. Sencillamente editaremos nuestro ya conocido archivo de configuracin php.ini y quitaremos el ; que comenta la lnea que referencia a dicha extensin, quedando as:
extension=php_mbstring.dll

Para que el servidor Apache lea nuevamente el archivo php.ini har falta guardar el cambio, y reiniciar el servicio. Luego refrescaremos la ventana del navegador y el error desaparecer.

Lo que se viene
Finalmente tenemos todo el software necesario instalado y configurado. A partir de este punto se puede Figura 9 comenzar la creacin de nuestro sitio web, mediante cdigo HTML, insertando scripts PHP, JavaScript, y accediendo a MySQL para actualizar la informacin dinmicamente. Desde phpMyAdmin podremos crear tablas, usuarios, y actualizar la informacin. Pero tanto el proceso de creacin de tablas, como la creacin de scripts PHP estn sujetos a una serie de recomendaciones de seguridad mnimas, que obviamente no pasaremos por alto. En la prxima entrega de esta gua crearemos usuarios MySQL y las tablas necesarias para continuar trabajando. Luego veremos una introduccin al lenguaje PHP, escribiremos el HTML necesario para base de nuestros scripts PHP, y mediante los scripts actualizaremos las pginas en forma dinmica.

Si tienen recomendaciones, consejos, o comentarios tiles sobre esta gua o el futuro de ella, los animo a escribir a revista_sintesis@grupoalbor.com: Les aseguro que sern tenidas en cuenta.

Revista Sntesis - Junio de 2006

32

Mario Rodrguez - mario@grupoalbor.com

Software Libre

Mxima compresin con 7-Zip


Presentacin del compresor 7z, del gestor de ficheros 7-Zip y del algoritmo de compresin LZMA.
Hace unos aos, la forma habitual de realizar copias de seguridad eran los disquetes, que tambin constitua el medio utilizado para trasladar ficheros de una mquina a otra. Por la misma poca, las comunicaciones se tornaban muy lentas, en comparacin a como se hacen en la actualidad, debido a que se efectuaban va mdem. Para compensar todas esas limitaciones se utilizaban diversos mtodos. Entre ellos, el ms popular era el uso de compresores de ficheros, algo que hoy en da se sigue utilizando y que, probablemente, siga siendo la forma natural de intercambio de ficheros en los prximos aos. En cierta ocasin, buscando algo que comprimiese ms y mejor que lo que utilizaba (ARJ, ZIP, RAR,...) top con un compresor, de fuentes abiertas, del que no haba odo hablar hasta entonces: 7z. Ni la pgina desde donde lo descargu ni la documentacin que vena con l explicaban muy bien qu se supona que aportaba al mundo de los compresores. As que hice una prueba. Y me asust. Resulta que el tamao del fichero comprimido con 7z estaba entre la mitad y un tercio del tamao conseguido con cualquiera de los compresores que utilizaba entonces. Supuse, errneamente, que estaba ante un compresor con prdidas (semejante a las compresiones GIF, JPEG y dems) y que, por ello, consegua esas razones de compresin tan elevadas. Luego, al extraer del fichero comprimido, me di cuenta de que no era as: los ficheros eran idnticos a los originales. Desde entonces a hoy, el desarrollo del compresor 7z y de su formato de compresin, LZMA, no ha decado. Cuando se escribe esto, el formato es soportado tanto en Windows (32 y 64 bits) como en Linux. Adems, se ha agregado una interfaz de usuario (Figura 1), denominada 7Zip, que permite que las operaciones a realizar puedan hacerse a golpe de ratn, es decir, mucho ms amigablemente que el uso de la lnea de comandos que proporciona 7z.exe, aplicacin que tambin se distribuye, lo que permite que pueda ser utilizado en ficheros por lotes (BAT, por ejemplo, o guiones de la shell de Linux, mediante p7zip). Al instalar 7-Zip en Windows, ste registrar el formato 7z como propio, de tal forma que puedan abrirse los ficheros *.7z desde el propio Explorador de Windows. A travs de la interfaz de usuario de 7Zip pueden configurarse cada una

Figura 1.- Interfaz de usuario de 7-Zip

Revista Sntesis - Junio de 2006

33

Mxima compresin con 7-Zip

Software Libre

de las opciones de la aplicacin. La interfaz de usuario viene en varios idiomas, entre ellos el castellano. Para cambiar de un idioma a otro, basta con seleccionar el comando Opciones, del men Herramientas, y escoger el idioma con el que se desee trabajar. Adems, en el mismo dilogo de configuracin puede seleccionarse el modo en que aparecer 7-Zip en el Explorador de Windows. Desde la pestaa Plugins puede indicarse la forma en que ha de crearse la entrada del men contextual e incluso qu elementos deben aparecen en l, lo que permitir gestionar (crear, expandir,...) ficheros comprimidos desde el propio Explorador. La forma en que se registran los diferentes formatos de compresin soportados se establece desde la pestaa Sistema. Y es que, aparte del formato estndar de 7z, el denominado LZMA, se soportan los formatos de compresin ms utilizados, tal y como se muestra en la Figura 2. Algunos formatos pueden parecer extraos para los usuarios Windows. El formato deb es el utilizado para distribuir los paquetes en Debian GNU/Linux. (deb viene de Debian software package). En cuanto al formato rpm es el utilizado habitualmente en varias distribuciones Unix y Linux como, por ejemplo, Red Hat y el Proyecto Fedora. Los ficheros cpio son similares a los que se obtienen mediante tar, es decir, son formatos de empaquetado, ms que de compresin, y se crean mediante utilidades del mismo nombre, que estn disponibles tanto para Linux como para Windows. El algoritmo LZMA, utilizado por 7-Zip, est escrito en C++, aunque existen versiones del algoritmo para ser utilizadas desde C, C# y Java. Tal y como se indica en la pgina LZMA SDK, el algoritmo se distribuye cubierto por cualquiera de las cuatro licencias siguientes: GNU Lesser General Public License (GNU LGPL); Common Figura 2.- Configuracin de 7-Zip Public License (CPL), que es la misma que la que utiliza Eclipse; licencia simplificada para cdigo no modificable; y licencia propietaria, para lo que es necesario obtener permiso. Con ello, cualquier aplicacin puede utilizar la compresin LZMA. De hecho, cada vez son ms las aplicaciones que se distribuyen y/o que soportan la compresin LZMA: los instaladores Inno Setup y NSIS; el entorno de desarrollo Code::Blocks; la biblioteca JVCL (JEDI Visual Component Library); los compresores de ejecutables MEW y Upack (ver la comparativa con UPX); los compresores de ficheros IZArc y TUGZip, que pueden ser una alternativa a 7-Zip; el comparador de ficheros WinMerge, que se integra perfectamente con 7-Zip; etc. Sin nimo de que esto sea una comparativa aceptable, he hecho una prueba de compresin con varios compresores conocidos. La prueba se ha realizado a partir de un conjunto de ficheros de todo tipo (texto, PDF, imgenes, ficheros ejecutables, bibliotecas, otros ficheros comprimidos, fuentes,...), con un tamao total de 1.833.151.877 bytes. Se han utilizado los compresores que van en la tabla siguiente, ajustndolos para que compriman al mximo:

Revista Sntesis - Junio de 2006

34

Mxima compresin con 7-Zip

Software Libre

Compresor 7-Zip WinRAR Tar+Bzip2 Tar+gz WinZip 7-Zip

Formato LZMA RAR tar.bz2 tar.gz ZIP ZIP

Tamao 632.081.234 669.202.105 730.694.651 803.432.300 880.738.290 860.684.783

Razn (%) 34,48 36,51 39,86 43,83 48,05 46,95

Diferencia 0 5,87 15,6 27,11 39,34 36,17

La prueba de la ltima lnea se ha realizado para comprobar si, efectivamente, 7-Zip comprime en formato ZIP mejor que otros compresores, tal y como se dice en su propia pgina. Como se ve, ha aventajado a WinZip en algo ms de un 3%. En la pgina oficial de 7-Zip, en Wikipedia y en la pgina de Tukaani Linux Project pueden verse comparativas, que vienen a confirmar la prueba anterior: en estos momentos, LZMA es la que mayor razn de compresin proporciona. En cuanto a la velocidad de compresin, en la prueba que he hecho no la he medido. Adems, depende del sistema operativo y del momento en que est, es decir, de cmo responda en un momento dado. Tambin depende de la versin que se tenga de cada compresor. Mi impresin es que, en el aspecto de velocidad de compresin, el orden sera: Tar+gz, WinZip, 7-Zip (comprimiendo ZIP), WinRAR, 7-Zip (comprimiendo LZMA) y Tar+Bzip2. Pueden consultarse tiempos ms precisos en la pgina de comparativas de Tukaani. El dilogo de creacin de ficheros comprimidos (ver Figura 3) permite indicar el Formato de archivo a crear: 7z, Tar o Figura 3.- Dilogo de opciones de compresin Zip. En el caso de elegir la compresin 7z o Zip (Tar no tiene compresin), se permite especificar el Tipo de compresin o grado, entre Sin compresin y Ultra. El

Revista Sntesis - Junio de 2006

35

Mxima compresin con 7-Zip

Software Libre

Tamao del diccionario afecta al resultado final: a ms tamao, mayor compresin. Tambin afecta al consumo de memoria, tanto en el momento de la compresin como en el de la descompresin. Este punto es importante ya que, si se elige un Tamao del diccionario que implique ms Memoria usada para Comprimir que la fsica que tenga el sistema, se forzar al uso de memoria virtual y, segn lo que he probado, al hacerlo, se ralentiza el sistema hasta un punto que puede llegar a ser insoportable. Asimismo, si el Tamao del diccionario implica la necesidad de tener una Memoria usada para Descomprimir mayor que la que tenga la mquina destino, es posible que no pueda extraerse nada, por falta de memoria. Por ello, es interesante ver que, en el dilogo de creacin ( Figura 3), se indica cunta memoria se necesita, tanto para comprimir como para descomprimir. Resumiendo, la compresin LZMA est lo suficientemente madura como para ser utilizada en cualquier aplicacin que necesite trabajar con ficheros comprimidos. Quiz se echa en falta una biblioteca (LIB, DLL,...) que pueda vincularse, esttica o dinmicamente, a las aplicaciones, pero supongo que se llegar a ello. Entretanto, bastar con agregar los ficheros de LZMA SDK en las aplicaciones.

Referencias:

Pgina oficial de 7-Zip: http://www.7-zip.org/ Descarga: http://www.7-zip.org/download.html Pgina en castellano de 7-Zip: http://www.7-zip.org/es/ Sobre el formato LZMA: http://www.7-zip.org/es/sdk.html 7-Zip en Wikipedia: http://es.wikipedia.org/wiki/7-Zip y http://es.wikipedia.org/wiki/7z

Revista Sntesis - Junio de 2006

36

Mario Rodrguez - mario@grupoalbor.com

C++

Contenedores asociativos (y IV)


Se tratan los contenedores asociativos de clave mltiple, multimap y multiset, junto con algunos consejos para la eleccin del contenedor ms apropiado para un determinado uso.
Para poder manipular de manera cmoda y sencilla los elementos de un mismo tipo, se almacenan stos en unas estructuras de datos que, de manera general, reciben el nombre de contenedores. La caracterstica comn a todos ellos es que contienen elementos de un mismo tipo. Las diferencias ms evidentes se refieren a la forma en que se almacenan los elementos, internamente, y a cmo se accede a ellos, posteriormente. Todos los contenedores presentan los elementos contenidos como una secuencia (ver la Figura 1), no necesariamente ordenada. El concepto de secuencia es, pues, inherente al de contenedor. Los elementos se van aadiendo, de una manera u otra, a la estructura de datos. Finalmente, cuando se accede a ellos, son recuperados uno tras otro7, bien en el orden en que se aadieron o bien en otro orden distinto, sea ste impuesto por nosotros8 o por el propio contenedor.

Figura 1.- Secuencias y contenedores

Ante un problema dado, la eleccin entre un contenedor y otro no es sencilla, ya que casi siempre puede encontrarse ms de una alternativa, dada la similitud existente entre contenedores. Por ese motivo, la eleccin de un modelo de contenedor sobre otro debe tener en cuenta algunas consideraciones adicionales. Entre todas ellas, la que inicialmente suele tomar mayor importancia es la del rendimiento o velocidad de respuesta de la estructura a los requerimientos de la aplicacin. Adems, suele valorarse la facilidad de uso, el consumo de recursos9, etc. Sin embargo, cuando dos contenedores tienen caractersticas semejantes, el factor determinante para decantarse por uno u otro son las operaciones que se vayan a efectuar con los elementos contenidos. Los contenedores de la biblioteca estndar (conocida como STL, de Standard Template Library) se han diseado de tal forma que se cumplan las tres mximas: alto rendimiento, bajo consumo de recursos y facilidad de uso. Lo habitual es que alguna de esas caractersticas sea predominante en un contenedor dado. Por ejemplo, un
7 Algunos contenedores, como array, vector o map, permiten el acceso directo a los elementos, cosa que se denomina acceso aleatorio. Casi todos los contenedores proporcionan acceso secuencial. La excepcin se encuentra en las colas y pilas (ver el N 6 de la Revista Sntesis), que slo permiten el acceso a un elemento dado. Por ejemplo, a la cabeza (front()) o al final (back()) de la cola. 8 Ver, como ejemplo, el que se muestra en el apartado El contenedor map, en la pgina 104 y siguientes del N 17 de la Revista Sntesis, donde se ordena un contenedor map de int por pares e impares. 9 Me refiero a la cantidad de memoria que las rutinas y estructuras del contenedor necesitan.

Revista Sntesis - Junio de 2006

37

Contenedores asociativos (y IV)

C++

contenedor asociativo, como map o set, proporciona mayor rapidez, tanto en bsquedas como en inserciones, que un contenedor de secuencia10, como vector o list, a costa de un mayor consumo de recursos. Por ello, para obtener lo mejor de estas caractersticas es necesario que se elija aquel contenedor que mejor se adapte a los requerimientos de la aplicacin donde se pretende utilizar. Los algoritmos11 que operan con los elementos se han ido optimizando con la utilizacin de los contenedores en diversos ambientes, lo que redunda tanto en el rendimiento como en la seguridad de uso. Por otro lado, la mayor parte de los contenedores proporcionan funciones con nombres y efectos idnticos. Es decir, tienen una interfaz similar, lo que hace que la curva de aprendizaje sea mnima: conocer un contenedor hace que el siguiente sea ms sencillo de entender y utilizar. As que, en la eleccin de uno u otro, toman un papel relevante las principales operaciones a realizar con los elementos. La estructura de datos ms sencilla es un array o matriz. La ventaja que tiene sobre el resto es su rapidez de acceso, ya que basta una sencilla suma (de tamao del elemento contenido) o mltiplo de ella para situarse en un elemento concreto de la secuencia. Las principales desventajas de esta estructura son la de ser esttica y pasiva: su tamao es fijo y todas las operaciones que vayan a realizarse han de estar bien codificadas por el programador. Un array es un mero contenedor de elementos del mismo tipo. Como tal, no es capaz de ajustar su tamao para albergar ms o menos elementos que los inicialmente considerados por el programador. Puede declararse un array dinmico, que permita ampliar o reducir los elementos contenidos. En este caso, habr que programar la adquisicin de memoria, la copia y eliminacin de elementos y la liberacin de memoria. Y eso, para cada tipo de array dinmico que la aplicacin vaya a manejar. Cercano en rapidez de respuesta y en consumo de memoria a un array, est el contenedor vector12 de la biblioteca estndar. Lo que un vector pierde de rapidez con respecto a un array, lo gana en facilidad de uso y seguridad. No es necesario que se programe, de antemano, el nmero de elementos a contener en el vector, ya que ste es capaz de ajustar su tamao a nuevos requerimientos (ms o menos elementos). Adems, si los elementos contenidos son objetos (si no son punteros a objetos), no har falta programar la lgica de creacin y destruccin de dichos objetos, ya que se encuentra incorporada dentro del propio contenedor. Dicho de otro modo, a diferencia de un array, un vector es un contenedor dinmico, como el resto de los que conforman la biblioteca estndar. Este contenedor es, pues, la alternativa clara a un array cuando el rendimiento de la aplicacin no es crtico13. Al igual que en un array, el rendimiento de un vector decae cuando se han de agregar, eliminar o insertar elementos en una posicin distinta a la del final del contenedor. En el caso de una insercin, tanto en un array como en un vector, suele ser necesario obtener ms memoria para, a continuacin, insertar el nuevo elemento en su posicin y copiar los que le siguen en la secuencia. En el caso de una eliminacin, la copia se har antes de la eliminacin (se pasan todos los elementos que siguen al eliminado una posicin hacia adelante en la secuencia), liberando finalmente el ltimo elemento. Como es comprensible, la copia o movimiento de esos elementos depende directamente del nmero de elementos existente en el contenedor: podemos imaginar un bucle que recorra todos los elementos, desde el insertado o eliminado hasta el final de la secuencia, trasladando elementos un lugar atrs (insercin) o adelante (eliminacin). Debido a que es habitual necesitar un contenedor en el que puedan agregarse o eliminarse elementos de una posicin arbitraria de la secuencia, la STL proporciona el contenedor list14, especialmente diseado para insertar o eliminar elementos en cualquier lugar, sin que el nmero de elementos contenido afecte al tiempo de ejecucin 15. Esta flexibilidad se gana a costa de velocidad en el acceso a los elementos. Como consecuencia, en una list no puede hacerse referencia a un elemento mediante el operador de acceso (operator[]), tal y como se hace en un
10 Los diferentes tipos de contenedores se presentaron en la pgina 45 y siguientes del N 3 de la Revista Sntesis. 11 Los algoritmos pueden ser independientes del contenedor, como los tratados en los nmeros 7 a 13 de la Revista Sntesis, o pueden ser parte del contenedor, tal y como se ha visto al tratar cada uno de ellos (vector, list, map,...). 12 Ver el artculo El contenedor vector, en la pgina 54 y siguientes del N 4 de la Revista Sntesis. 13 Se puede elegir un array antes que un vector cuando se intenta ahorrar alguna millonsima de segundo en el acceso a los elementos. Y, an as, un buen perfilador nos dir si el cambio tiene algn efecto apreciable. Excepto para aplicaciones muy concretas, lo ms probable es que, la diferencia entre uno y otro, sea mnima. 14 Ver el artculo El contenedor list, en la pgina 57 y siguientes del N 5 de la Revista Sntesis. 15 Los elementos que siguen al insertado o eliminado no se copian, como se hace en un array o en un vector, sino que se enlazan: el funcionamiento de una list es similar al de una lista de doble enlace.

Revista Sntesis - Junio de 2006

38

Contenedores asociativos (y IV)

C++

array o en vector. Para ello, el contenedor list proporciona iteradores (aunque no se ha dicho, tambin vector los proporciona, as como la mayora de los contenedores de la STL), que permiten pasar (iterar) por la secuencia a la bsqueda del elemento deseado. A medio camino entre un vector y una list, se encuentra el contenedor deque16, que est especialmente diseado para aquellas aplicaciones que requieran eliminar o insertar elementos tanto al inicio como al final de la secuencia. En este contenedor, el rendimiento en el acceso a los elementos se aproxima al de vector (proporciona el operador de acceso, operator[]) y las inserciones, a ambos lados de la secuencia, se aproxima al de list. Los adaptadores de secuencia17, como stack (pila), queue (cola) o priority_queue (cola de prioridades), utilizan deque para almacenar sus elementos. En la Figura 1 se muestran dos secuencias idnticas y la manera en que son tratadas por los contenedores. Hay que hacer la salvedad de que, como se ha dicho antes, list no posee operator[]. Es decir, los nmeros de ndice (0, 1 y 2) que van bajo la secuencia superior slo son aplicables a contenedores que proporcionen ese operador, como array, vector o deque. Las flechas que, en la figura, van de un elemento a otro (A B C) indican que puede recorrerse la secuencia en ambos sentidos. Ninguno de los contenedores de secuencia (vector, list o deque) son rpidos en una bsqueda. Los contenedores vector y deque, que proporcionan el operador de acceso (operator[]), son ptimos para aquellas aplicaciones en las que se sabe sobre qu elemento de la secuencia se va a operar: para desplazarse al elemento deseado, se incrementa el ndice del contenedor un nmero de veces ( operator[x], donde x es el ndice), tras de lo que ya puede trabajarse con el elemento. Esto es, funcionan como un array. Por su parte, el contenedor list es el mejor de todos cuando la operacin principal (la que ms veces va a utilizarse) es la de eliminacin o la de insercin de elementos en cualquier lugar de la secuencia. En todos ellos (incluido array), buscar un elemento consiste en iterar por toda la secuencia, desde su inicio a su fin. Ni los arrays ni los contenedores vector, list y deque imponen un orden a los elementos contenidos. En estos contenedores, el orden en que se inserten los elementos ser el orden en que quedarn en la secuencia. En todos ellos puede utilizarse algn tipo de algoritmo que obligue a que los elementos se dispongan en un orden preestablecido. Por ejemplo, los elementos pueden obtenerse y agregarse a estos contenedores a partir de una consulta, ya ordenada, a una base de datos. Sea como sea, tanto el algoritmo de insercin que se utilice como el orden de los elementos no es responsabilidad del contenedor, sino del usuario (programador). Si se tiene uno de estos contenedores, con los elementos ordenados, el hecho de agregar un nuevo elemento (por ejemplo, con la funcin push_back() de un vector), no asegura que el contenedor contine teniendo los elementos ordenados tras la insercin. Lo ms probable es que no sea as. Y que, adems, no importe: al fin y al cabo, si se ha escogido ese tipo de contenedor es porque, precisamente, no importa el orden. Sin embargo, muchas aplicaciones necesitan mantener la secuencia en un orden determinado, independientemente del orden en que se agreguen o eliminen elementos de la secuencia. Por ello, la biblioteca estndar proporciona un conjunto de contenedores, que realizan automticamente el ordenamiento de elementos, y que se conocen con el nombre general de contenedores asociativos. Un contenedor asociativo es aquel que vincula o asocia un valor a otro valor. Al primer valor se le conoce con el nombre de clave, debido a que es por donde se ordena y busca en el contenedor, mientras que al segundo valor se le denomina valor mapeado o valor asociado18 a la clave. Este ltimo valor es posible que no exista en un contenedor asociativo, tal y como sucede en set19, que tiene clave, pero no valor mapeado (en la secuencia de la parte inferior de la Figura 1, los elementos dA, dB y dC, que corresponden al valor mapeado, no existiran en un set ni, como veremos enseguida, en un multiset). Pero ambos elementos, clave y valor mapeado, aparecen en map20: en este contenedor, como en multimap, del que se hablar enseguida, s existen los elementos dA, dB y dC de la Figura 1.
16 Ver el artculo Colas y pilas, en la pgina 72 y siguientes del N 6 de la Revista Sntesis. 17 Ver el apartado Adaptadores de secuencia, del artculo Colas y pilas, en la pgina 76 y siguientes del N 6 de la Revista Sntesis, donde se habla de stack, queue y priority_queue. 18 En lo que sigue, se utilizarn los trminos valor asociado y valor mapeado indistintamente. 19 Ver el artculo Contenedores asociativos (II), en la pgina 68 y siguientes del N 18 de la Revista Sntesis. 20 Ver el artculo Contenedores asociativos (I), en la pgina 98 y siguientes del N 17 de la Revista Sntesis.

Revista Sntesis - Junio de 2006

39

Contenedores asociativos (y IV)

C++

Quiz sorprenda un poco que a set y multiset se les considere contenedores asociativos, cuando no asocian la clave con nada. Al hablar de asociacin se ha de entender como la posibilidad de asociar. En un map, por ejemplo, s puede tenerse un valor asociado con la clave. Pero tambin puede no tenerse, bien porque ste sea nulo o bien porque no interese su valor. Siendo as, podra elegirse un set, en vez de un map, aunque no hay ninguna regla que prohba su uso en esas circunstancias. Todos los contenedores asociativos son idnticos en lo que se refiere a inserciones, eliminaciones, velocidad de bsqueda, facilidad de manejo y consumo de recursos. As que, cuando el valor asociado no sea interesante, elegir uno u otro es casi cuestin de gustos. Y, cuando s interese el valor asociado, no podr elegirse ni un set ni un multiset, dado que no disponen de la posibilidad de almacenarlo en ningn lado. Los contenedores asociativos tienen la caracterstica de que los elementos que se agreguen se disponen en un orden que, adems, puede ser especificado por el usuario del contenedor21. Una vez declarada la variable del contenedor asociativo, al agregar un elemento, el contenedor buscar el lugar que le corresponda en la secuencia, segn el orden especificado en la declaracin, y le colocar en esa posicin. El resultado es que si se pasa por los elementos contenidos a continuacin, operacin que siempre se realiza a travs de iteradores, el contenedor (ms precisamente, el iterador) devolver los elementos en el orden del contenedor22. Si bien se da la posibilidad al usuario de realizar la bsqueda del lugar de insercin de nuevos elementos por el criterio que desee, el comportamiento predeterminado es hacerlo por la clave del contenedor y, en concreto, a travs del uso del predicado less23 (menor que), de la STL. Es decir, un contenedor asociativo ordenar automticamente sus elementos, de menor a mayor, segn su clave, sea lo que sea lo que signifique menor que entre los elementos contenidos, cosa que puede programarse. De ah que, cuando no existe valor asociado alguno, el funcionamiento y comportamiento sea idntico en map, set, multimap, multiset. Con respecto a los contenedores de secuencia (vector, list o deque), los contenedores asociativos tienen la ventaja de la rapidez, tanto en las bsquedas como en las inserciones o eliminaciones. Y tienen la desventaja de un mayor consumo de recursos (memoria). Los contenedores asociativos son preferibles cuando se va a utilizar el contenedor para bsquedas. Es decir, se rellena el contenedor y, a continuacin, se hacen operaciones con sus elementos. Por ejemplo, si se van a realizar operaciones matemticas (sumas, desviaciones,...), de agrupamiento (por ejemplo, filtrar elementos a travs de una clave o un rango de claves), de relacin (por ejemplo, asociar un dato con su representacin visual), etc. A aquellos contenedores que, como map y set, asocian un nico valor de clave con un valor mapeado (aunque, tal y como sucede en set, ste no exista), se les denomina contenedores asociativos de clave nica. Significa esto que no pueden agregarse dos elementos con la misma clave (en teora, al menos: ver la referencia de la nota 22) o, dicho de otro modo, el contenedor rechazar cualquier intento de agregar un elemento si ya existe otro con la misma clave. Este rechazo no se recibir como una excepcin (la STL apenas lanza excepciones y, cuando lo hace, est bien documentado, tanto el momento como la razn y el tipo, en el propio lenguaje24), sino que sencillamente el elemento no se agrega a la secuencia.
21 En el apartado El contenedor map, de la pgina 104 y siguientes del N 17 de la Revista Sntesis, se ordena un contenedor map de int por pares, primero, e impares, despus. Es decir, no en el orden numrico habitual. 22 Una excepcin a esta regla se indic en el apartado Manejando iteradores, de la pgina 75 y siguientes del N 18 de la Revista Sntesis, donde se demostr la forma de hacer perder el orden impuesto a un contenedor set. 23 Sobre el predicado less se habl en el apartado Predicados de la STL, del artculo Algoritmos III, en la pgina 86 y siguientes del N 9 de la Revista Sntesis. Sobre cmo se aplica ese predicado a los contenedores asociativos se trat en el apartado El contenedor map, del artculo Contenedores asociativos (I), pgina 104 y siguientes del N 17 de la Revista Sntesis. 24 El captulo 15 de Working Draft, Standard for Programming Language C++ trata sobre Exception handling (en el momento en que se escribe esto, puede descargarse el borrador del estndar desde la direccin http://www.openstd.org/jtc1/sc22/wg21/docs/papers/2005/n1905.pdf y, en cualquier caso, siempre podr leerse en http://www.openstd.org/jtc1/sc22/wg21/). En el apartado 23.1 (Container requirements), punto 11, de ese mismo borrador se habla sobre las condiciones que pueden provocar una excepcin en los contenedores de la biblioteca. Por su parte, Bjarne Stroustrup, en el apartado E.4.1 Insertion and Removal of Elements de Appendix E: Standard-Library Exception Safety (ese apndice, en el momento de escribir esto, puede ser descargado desde http://www.research.att.com/~bs/3rd_safe.pdf), del libro The C++ Programming Language, Special Edition, explica en qu condiciones los algoritmos de los contenedores pueden lanzar excepciones.

Revista Sntesis - Junio de 2006

40

Contenedores asociativos (y IV)

C++

Sin embargo, en algunas aplicaciones es posible que se necesiten almacenar varios elementos que tengan la misma clave. A ttulo personal he de decir que nunca me he encontrado con la necesidad de semejante estructura de datos. Quiz sea por la forma en que programo, que siempre me lleva a elegir contenedores simples (vector o, como mucho, map). La cuestin es que no encuentro de mucha utilidad contenedores asociativos que permitan almacenar varias claves idnticas. Entindase esto como algo personal, ya que, si sesudas mentes han llegado a la conclusin de que son necesarios, por algo ser. El resultado de mi incapacidad se va a demostrar con los ejemplos que siguen, que parecern forzados. En todo caso, espero que sirvan como referencia del uso de este tipo de contenedores.

Contenedores asociativos de clave mltiple


Todos los contenedores asociativos basan su funcionamiento en un rbol binario equilibrado, de tipo negro rojo25, que permite claves mltiples y que se denomina __rb_tree, en la STL que acompaa a la versin 5 de C++Builder, y _Rb_tree, en versiones posteriores26. Es decir, realmente los contenedores asociativos no son los que contienen los elementos sino que es el rbol binario subyacente el que proporciona toda la funcionalidad, operando el contenedor asociativo como una especie de ajuste de interfaz entre el usuario del contenedor (el programador) y el rbol binario. Excepto porque es ms sencillo el manejo de un rbol binario a travs de la interfaz que presentan los contenedores asociativos, no hay ninguna razn que impida el uso del rbol binario directamente, esto es, sin que est de por medio un contenedor asociativo. Es lo que hicimos en la serie sobre los rboles binarios (ver las referencias de la nota 25). Si se revisan los ejemplos que acompaaron a esa serie y, en concreto, el fichero "ArbolBin.h", se ver la declaracin del rbol, adaptada a los fines del ejemplo propuesto entonces:
// Constructor del rbol binario del ejemplo arbol_binario(void) : __rb_tree< Clave, Clave, _KeyOf, Comparar, AllocMem >() , fNodoActual(0) { __insert_always = false; // Impide valores duplicados }

Como dice el comentario, el cambio en el valor de un campo bool del rbol binario le hace funcionar de un modo u otro. Con __insert_always puesto a true (valor por defecto), el rbol binario permite almacenar claves duplicadas. Con ese campo puesto a false, el rbol binario rechazar cualquier intento de agregar claves duplicadas. Puede comprobarse este punto cambiando el valor del campo en los ejemplos de la serie. Los contenedores asociativos de clave nica realizan esa misma tarea en su constructor (ver los ficheros correspondientes, <map.h> y <set.h>, en el directorio INCLUDE del compilador): poner a false el campo. Por su parte, los contenedores asociativos de clave mltiple, ponen a true dicho campo. Esa es, pues, la principal diferencia entre un tipo de contenedor asociativo y otro: los contenedores asociativos de clave nica, map y set, no permiten la existencia de claves duplicadas; los contenedores asociativos de clave mltiple, multimap y multiset, las permiten. E impedir o permitir claves mltiples es algo que se delega en el rbol binario (el campo __insert_always es del rbol binario), que es el que, en realidad, almacena los elementos y realiza las operaciones de los contenedores asociativos. Teniendo en cuenta lo anterior, no es extrao que pasar de un tipo de contenedor a otro sea sencillo. As, con muy pocos cambios en el cdigo, puede usarse un multimap donde antes se utilizaba un map. O un multiset, donde tenamos un set. Los cambios a realizar en el cdigo para pasar de un contenedor de clave nica a uno de clave mltiple suelen estar concentrados en aquellos algoritmos que se encargan de la bsqueda27 de elementos ya que,
25 De los rboles binarios se trat en la serie rboles binarios, en los nmeros 14, 15 y 16 de la Revista Sntesis. Los rboles binarios negro rojo se trataron en el apartado rboles negro rojo", del N 16. 26 Ver el apartado Una plantilla para un rbol binario, en la pgina 79 del N 14 de la Revista Sntesis. 27 Tambin hay una pequea diferencia en la forma en que se agregan nuevos elementos en los contenedores asociativos de clave mltiple. Pero, en general, las inserciones suelen concentrarse en una parte muy concreta del cdigo y, por tanto, los

Revista Sntesis - Junio de 2006

41

Contenedores asociativos (y IV)

C++

como veremos, la bsqueda en un contenedor de clave mltiple devolver un rango, mientras que la de uno de clave nica devuelve un elemento: el que tenga dicha clave nica. Pienso que se entendern mejor los contenedores de clave mltiple si ponemos un ejemplo. Como ya he indicado antes, me es francamente difcil encontrar ejemplos, cercanos a la realidad, del uso de este tipo de contenedores. Prcticamente en cualquier sistema que pensemos nos vamos a encontrar con elementos claramente identificables: un empleado tendr un cdigo (de empleado, de seguridad social, de telfono,...) por el que identificarle unvocamente; una calle, un nombre; un portal, un nmero; etc. En esas condiciones, los contenedores de clave nica (o incluso los contenedores simples, como array, vector,...) serviran mucho mejor que los contenedores de clave mltiple para almacenar esos elementos. Hay, sin embargo, un tipo de aplicacin para la que pueden ser de mucha utilidad. Se trata de todas aquellas que reciban la entrada de datos de manera desatendida (es decir, en aquellas en que no hay una persona ante la mquina) y que, adems, deban almacenar toda la entrada para su posterior proceso.

Requisitos del ejemplo


As que, para ilustrar el uso de los contenedores de clave mltiple, vamos a imaginar que nos han encargado realizar una aplicacin para procesar la contaminacin acstica en diversos puntos de una ciudad. Supondremos que algn organismo ha colocado, en puntos estratgicos, una serie de sensores capaces de registrar la velocidad de los vehculos que circulan por esos puntos y el nivel o intensidad sonora que originan. Cada vez que pasa un vehculo por el punto en que se haya situado un sensor, ste registra (podemos imaginar que el paso del vehculo acta a modo de disparador del sensor) la velocidad del vehculo y la intensidad sonora en ese instante. A continuacin, enva la informacin obtenida, a travs de algn medio (en el que no nos detendremos), a la mquina donde est nuestra aplicacin instalada. Los datos que llegan desde el sensor son los siguientes: El punto de la ciudad en que est situado el sensor, que se identificar con un cdigo nico que, por simplificar, supondremos numrico28. La fecha y hora en que se registr la informacin. Para nuestro ejemplo, no tiene importancia el formato en el que llegue este dato desde el sensor, aunque se supondr que pueden extraerse de alguna forma todos los componentes, tanto de la fecha (ao, mes y da) como de la hora (hora, minuto y segundo). La velocidad del vehculo, en kilmetros por hora. Ser un nmero en coma flotante (float). El nivel de ruido emitido en decibelios29 (db). Ser un nmero en coma flotante (float).

El hecho de que a cada sensor se le asigne un cdigo nico que lo identifique o, mejor dicho, que identifique el lugar donde se ha colocado el sensor, no significa que pueda convertirse en clave nica (de, por ejemplo, un map). Para descartar el cdigo del sensor (o cualquier combinacin del cdigo con el resto de los datos) como clave nica pensemos en que, si el sensor est situado en un punto donde slo existe un carril para que circulen los vehculos, pero estos viajan a velocidades elevadas, es posible que el mismo sensor registre dos o ms tomas (vehculos) en el mismo segundo. Si, donde se ha instalado el sensor, existe ms de un carril en el mismo sentido, las posibilidades
cambios a realizar para pasar de un tipo de contenedor a otro son ms sencillos que en la parte que trata con la bsqueda de elementos. 28 Este valor se podra utilizar, posteriormente, para hacer estudios sobre la contaminacin acstica en el punto en que se haya situado el sensor. 29 Aunque no tenga importancia para comprender el ejemplo, quiz interesen saber algunos datos sobre acstica. La intensidad de una onda de propagacin sonora se define como la energa que se propaga a travs de la unidad de rea por la unidad de tiempo. La intensidad de sonido, que se utiliza habitualmente como patrn, es Io = 1 w/cm2 = 10 erg/cm2 seg. El Bel es el logaritmo en base 10 de la relacin de dos potencias o intensidades. No obstante esta unidad resulta demasiado grande, por lo que se ha normalizado el uso de una unidad ms manejable: el decibelio. El decibelio (db) es, como su nombre indica, la dcima parte del Bel y es la unidad en que se mide habitualmente la intensidad o el volumen del sonido. El odo humano posee un umbral de audicin de 10-10 Io a 100 Io. Entre 100 Io y 1000 Io, es decir, a partir de unos 90 db, el que escucha siente dolor.

Revista Sntesis - Junio de 2006

42

Contenedores asociativos (y IV)

C++

de que un mismo sensor tome muestras en el mismo segundo se multiplicarn. Y si existe la posibilidad de que el sensor tome muestras de varios puntos (imaginemos que est colocado en un cruce de caminos), el nmero de muestras que puede tomar y enviar en el mismo segundo el mismo sensor sern impredecibles. Con esos datos, nos piden que realicemos una aplicacin que muestre, a modo de rbol, las lecturas de los sensores (ver Figura 2). El rbol debe permitir navegar a travs de los aos, meses y das que correspondan con las tomas de los sensores. Una vez se ha seleccionado un nodo en el rbol, en una tabla se mostrarn los datos del ao, mes y da que correspondan al nodo seleccionado. En la tabla debe ser posible resumir, a modo de medias, los datos de la velocidad (km/h) e intensidad del sonido (db, de decibelios), por hora, Figura 2.- La aplicacin de ejemplo en ejecucin minutos y/o segundos, adems de poder mostrar todos los datos del da. Por otro lado, ese mismo tipo de filtro o resumen debe ser aplicable al mostrar los datos recopilados en un grfico30, que muestre la evolucin de las tomas a lo largo del da, mes o ao que corresponda al nodo del rbol. No vamos a entrar en un anlisis ms profundo de la aplicacin a realizar. Daremos por supuesto que ya se ha realizado el anlisis, es decir, que se conocen todos los requisitos de la aplicacin, y nos concentraremos en el manejo de los datos, que llegan de los sensores tal y como se han explicado antes.

Declaracin del multimap


Si nos fijamos en la Figura 2 veremos que los datos de la fecha estn en el rbol, mientras que el resto se encuentran en la tabla (y, aunque en la figura no se ve, tambin en el grfico). Prcticamente est clara cul debe ser la forma que adopte el contenedor31: vinculado con cada nodo del rbol debe haber un valor (la clave), que
30 Cuando se solicita realizar una aplicacin, rara vez se llega a tanto nivel de detalle. Por ejemplo, que hay que realizar un grfico con los datos recopilados, suele ser algo que se sabe unos meses, semanas, das o incluso horas despus de haber hecho todo lo anterior y, probablemente, con la aplicacin ya en marcha. Aqu no se est diciendo nada sobre imprimir o exportar los datos recopilados. En una aplicacin real, habra que tener en cuenta todo eso. 31 Disear las clases a partir de una representacin visual (como la de la Figura 2) de lo que nos piden no es el mejor mtodo posible, sino todo lo contrario. Este desliz, consciente, se introduce slo por claridad en la exposicin. De hecho, el ejemplo se desarroll al contrario de como se est explicando: la forma que adopta ahora el formulario lleg al final, tras muchos cambios. Las estructuras de datos, que se presentan en lo que sigue, se decidieron desde el inicio, a partir de saber qu datos haba que almacenar, y, excepto por las funciones que se crearon para preparar su presentacin en pantalla, no han variado desde entonces.

Revista Sntesis - Junio de 2006

43

Contenedores asociativos (y IV)

C++

indicar qu datos (valor mapeado) deben mostrarse cuando se elija un nodo. Es decir, hemos de asociar el hecho de que el usuario seleccione un nodo, con los datos que representen a dicho nodo. Adems, vamos a necesitar que la bsqueda de esos datos sea rpida. Hemos de pensar en que los datos pueden ser cientos o miles por da (si un sensor toma una muestra por segundo, tendremos 3.600 datos por hora, slo de ese sensor32), con lo que, cuanto ms agrupados se encuentren en la secuencia, ms fcil ser su localizacin, cuando tengamos que hacer operaciones con ellos (recordemos que hemos de mostrar las medias tanto de los km/h como de los decibelios). Si los datos llegasen en un orden dado (por ejemplo, si siempre se obtuviesen ordenados por el momento de la toma), una alternativa a tener un contenedor asociativo sera un vector, donde se podra utilizar la bsqueda binaria con, por ejemplo, el algoritmo binary_search()33, para delimitar aquellos registros que nos interesen. Sin embargo, por las caractersticas de la toma de datos se puede inferir que no va a ser as. Aunque la toma de un sensor sea secuencial (no se sabe!), es decir, que enve los datos en el orden en que los toma, basta con que haya un par de sensores para que las tomas de ambos se intercalen en la llegada. Y quiz no en orden. Por ejemplo: el primer sensor se conecta a la mquina donde tenemos la aplicacin, enva los datos de los ltimos cinco minutos y desconecta; en ese momento hace lo propio el segundo sensor; etc. Es posible que todo lo anterior pueda deducirse mediante un anlisis previo del funcionamiento de los sensores. Pero confiar en ello para realizar la aplicacin sera un error de diseo. La aplicacin no trabaja con sensores sino con los datos que stos proporcionan. Ligar ahora el funcionamiento de la aplicacin a cmo trabajen los sensores la invalidar si, en el futuro, cambian los sensores (por unos ms modernos, por ejemplo) o si se desea que, en vez de llegar los datos desde los sensores, se carguen desde disco. Necesitamos, pues, de un contenedor que siempre mantenga ordenados los datos, lleguen como lleguen y, por tanto, se inserten en el orden en que se inserten en l. La eleccin apropiada es, sin duda, un contenedor asociativo. Sin embargo, como se ha dicho antes, de los datos que vamos a almacenar, no tenemos nada que sea nico y que, por tanto, pueda convertirse en clave nica. As que hemos de optar por un contenedor asociativo de clave mltiple, esto es, que nos permita almacenar tomas duplicadas. De lo anterior espero que no se deduzca que el contenedor escogido es el nico posible. Existen otras alternativas (la del vector junto a binary_search(), me gusta especialmente) que se descartan, ms por centrarse en el ejemplo que por invlidas. Vamos a escoger, como estructura de almacenamiento, un multimap que posea una clave compuesta por el da, mes y ao de la toma y, como valor asociado, el resto de los datos (km/h, decibelios, etc.). La eleccin de la clave es la parte ms delicada e importante porque un multimap, como el resto de los contenedores asociativos, ordenar los elementos automticamente por dicha clave. Por lo dems, la declaracin de un multimap se realiza como la de un map34, es decir, especificando el valor que actuar de clave y el valor que estar asociado a la clave.
// Estructura con los datos tomados por el sensor struct datos_sensor { /* ... */ }; // Sinnimo para el contenedor 'multimap': clave de tipo // 'std::time_t' y valor asociado de tipo 'datos_sensor' typedef std::multimap< std::time_t, datos_sensor > tipo_mapa; // Variable de tipo 'multimap' tipo_mapa fMapaSensor;

32 En este ejemplo supondremos que hay memoria suficiente como para tener todos los datos en ella. Es decir, que no hace falta almacenar los datos en disco y leerlos desde all cuando hagan falta. Si, como ampliacin, en el futuro se viese que son demasiados datos, podramos crear una clase que se encargase de guardar y suministrar los datos a la aplicacin cuando sta los requiriese. En esas condiciones, el funcionamiento de lo que se explica a continuacin no necesitara apenas cambios. 33 El algoritmo binary_search() se explic en el N 13 de la Revista Sntesis, pgina 91 y siguientes. 34 Ver una declaracin equivalente de un map en la pgina 72 del N 19 de la Revista Sntesis.

Revista Sntesis - Junio de 2006

44

Contenedores asociativos (y IV)

C++

Se ha escogido como clave el tipo time_t, definido en el espacio de nombres std, porque necesitamos cierta compatibilidad con los datos a tratar (una fecha). El tipo time_t, que se declara como long en el fichero <time.h>, del directorio INCLUDE del compilador, es todo lo que se necesita para almacenar una fecha (ao, mes y da). Una declaracin ms oficial hubiese sido establecer la clave al tipo TDateTime, del espacio de nombres System de la VCL, o a la estructura tm, de la biblioteca estndar. No se ha hecho as porque, para los requerimientos de la aplicacin, no se necesitaba una declaracin de fecha oficial. La fecha, como tal, slo aparece en la etiqueta que va sobre la tabla, tras darle formato (ver Figura 2). La mayor parte de los clculos se realizan con los componentes de la fecha: el ao, el mes y/o el da. Por ello, tener que codificar la fecha (con, por ejemplo, la funcin EncodeDate(), del espacio de nombres Sysutils) y decodificarla (con, por ejemplo, DecodeDate(), del espacio de nombres Sysutils) hubiese supuesto una merma en el rendimiento. Y, de haber escogido como clave la estructura tm, tendramos que haber definido el operador de comparacin (habitualmente, menor que) entre estructuras. As que, para manipular la fecha almacenada en la clave del contenedor, se ha preferido una clase creada especialmente para la ocasin, a la que se ha denominado datos_fecha. Las interioridades de esta clase no son relevantes para lo que vamos a tratar. En todo caso, todo el cdigo que se ver se encuentra en los ficheros "SensoFRM.cpp" y "SensoFRM.h", de los ejemplos que acompaan a este artculo. De la clase datos_fecha lo nico que necesitamos saber es que posee mtodos para asignar y recuperar los componentes de la fecha: ao (set_ano() y get_ano()), mes (set_mes() y get_mes()) y da (set_dia() y get_dia()). Con estos mtodos puede transformarse la fecha que contiene la clase datos_fecha a cualquier otra, ms oficial (por ejemplo, a TDateTime o a la estructura tm, de la biblioteca estndar). Y viceversa. Tambin proporciona mtodos para establecer la fecha al comienzo o final del mes o ao actual35 (a_inicio_de_mes(), a_fin_de_mes(), a_inicio_de_ano() y a_fin_de_ano(), respectivamente), algo que se utilizar ms adelante para fijar los lmites del rango de datos a tratar. En cuanto al valor mapeado del multimap se ha escogido una estructura, a la que se ha denominado datos_sensor, que tiene una declaracin de lo ms evidente, por mucho que el primer campo (fIdSensor) sea sospechoso de provocar conflictos si hay, ahora o en el futuro, ms de 256 sensores:
struct datos_sensor { unsigned char fIdSensor; unsigned char fHora; unsigned char fMinutos; unsigned char fSegundos; float fKmHora; float fDecibelios; //... };

// // // // // //

Identificador del lugar en que est situado el sensor Hora de la toma Minutos de la toma Segundos de la toma Velocidad del vehculo, en km/hora Intensidad del sonido, en decibelios

Como se ver, en este ejemplo el multimap no toca los datos del valor mapeado, aunque no siempre es as. La razn de escoger una struct (con campos pblicos), en vez de una class (con funciones de acceso a los datos) es que no encontr el invariante36 de la supuesta clase. Los campos no parecen tener relacin entre ellos exceptuando, quiz, a los componentes de hora, minuto y segundo que, tomados como tales, constituiran una clase, similar a
35 La clase, tal y como est (sin pulir ni optimizar), puede ser utilizada en otros proyectos. En buena lgica, debera haberse puesto en su propio mdulo pero se ha preferido dejar todo el cdigo de este ejemplo en un mismo fichero para que sea cmoda su lectura. En los comentarios que encabezan la clase datos_fecha, en el fichero "SensoFRM.h", se puede leer: La clase necesita de los enumerados etiq_meses y etiq_dias, que se han declarado en TFrmTomasSensor para que puedan utilizarse sin tener que cualificarlos completamente (de esa manera, en vez de escribir "datos_fecha::Enero" se escribe "Enero", sin ms). Si quiere sacarse la clase a un fichero aparte (un *.H, por ejemplo) bastar con copiar completamente la clase y esos enumerados. 36 Sobre los invariantes de clase se habl en el apartado Una secuencia de empleados, en la pgina 72 y siguientes del N 19 de la Revista Sntesis, mientras se analizaba la clase empleado.

Revista Sntesis - Junio de 2006

45

Contenedores asociativos (y IV)

C++

datos_fecha, presentada antes. No se hizo as para no complicar el ejemplo pero, sobretodo, porque durante el manejo del contenedor no se encontr necesidad alguna de hacerlo. Por suerte (relativa), la clase se declara y define en la parte privada del formulario donde se utiliza y en ningn momento se deja a la vista que se est utilizando: ninguna funcin del formulario proporciona acceso a esos datos, cosa que, por s misma, no quiere decir nada: maana mismo es posible que cambien los requisitos de la aplicacin y que se tengan que proporcionar datos_sensor a otras clases. Ser entonces cuando tengamos que afirmar que hemos hecho un mal diseo de la clase que acta de valor mapeado.

Agregar elementos al multimap


Esta es la principal diferencia entre un map y un multimap: la forma en que se agregan elementos al contenedor. Para insertar un nuevo elemento en un map se utiliza el operador de acceso (operator[]). Tomando el ejemplo del artculo anterior37 se hara:
// Agregar, al "'map' de empleados", uno de "clave" 'c' y de "valor mapeado" 'v' mEmpleado[c] = v;

La lnea anterior se puede leer como dice el comentario. Al empleado se le identifica por el cdigo (clave del contenedor) que contenga la variable c y sus datos (valor asociado) son los que contenga la variable v. Dado que en un map la clave es nica, existe una relacin directa y unvoca entre c y v: en el contenedor mEmpleado no puede haber dos elementos con la misma clave c. Por ello, es sencillo tomar una determinacin ante una sentencia como la de la lnea anterior. En el contenedor map esa sentencia tiene uno de los dos efectos siguientes: si el empleado de clave c existe en el contenedor mEmpleado, sus datos cambiarn a v; si el contenedor no tiene un empleado de clave c, se agrega uno con esa clave para, a continuacin, asociarle el valor v. Ahora bien, si se permitiesen claves duplicadas (varios valores de c iguales), como sucede en un contenedor multimap, qu determinacin tomar? Si el empleado de clave c no existiese, podra hacerse como en map, es decir, agregar uno y asociarle el valor v. Pero, si existe, a cul de los c se le asocia v? a todos? (todos los empleados de clave c cambiaran sus datos a v!) a ninguno? Si se ignorase el que ya haya algn empleado de clave c y se agregase uno nuevo38, se tendra muy poco control sobre el contenedor que, silenciosamente, duplicara los datos, cuando slo pretendamos cambiar el valor asociado. Para evitar esos conflictos, el contenedor multimap no proporciona el operador de acceso operator[]. Para agregar elementos al contenedor, el mtodo que se proporciona es el mismo que en set39 (y que en multiset, como veremos): utilizar la funcin insert() del contenedor. Lo que se inserta con esa funcin en el contenedor multimap es tanto la clave como el valor mapeado (en set y multiset no existe valor mapeado, as que slo se inserta la clave). En el fichero <map.h> se puede ver la forma en que est declarada la funcin dentro del contenedor:
template < class Key, class T, class Compare = less< Key >, class Allocator = allocator< pair<Key, T> > > class multimap { // // // // Clave Valor mapeado Mtodo de comparacin: "menor que" Localizador de memoria

37 Ver la pgina 71 del N 19 de la Revista Sntesis. 38 Notar que, si no se agregase un nuevo elemento, el contenedor multimap actuara como uno de clave nica. 39 Ver el apartado El contenedor set, en la pgina 69 y siguientes del N 18 de la Revista Sntesis.

Revista Sntesis - Junio de 2006

46

Contenedores asociativos (y IV)

C++

public: // Sinnimo typedef pair<Key, T> value_type; // Agregar un elemento al contenedor (nota: '__t' es el rbol binario subyacente) iterator insert(const value_type& x) { return __t.insert(x).first; } //... };

La clase de plantilla pair ya ha aparecido en artculos anteriores40. En el argumento x de la funcin insert() de multimap, el primer elemento del pair (first) contiene la clave del contenedor y, el segundo elemento del pair (second) contiene el valor mapeado. La funcin insert() devuelve un iterador (iterator, segn la declaracin que hemos puesto antes), que apunta al elemento que se ha insertado: excepto por falta de memoria, siempre se agrega el elemento x al contenedor. Lo mismo sucede en multiset. Pero en map y en set no siempre es as ya que, como se ha dicho, en estos contenedores no se admiten claves duplicadas. Existen varias formas de crear el pair que necesita la funcin insert(), unas ms complicadas que otras. La manera que me parece ms sencilla es utilizar la funcin make_pair(), de la biblioteca estndar, ya que con ella queda claro el propsito de lo que se pretende (crear pair). La funcin make_pair() admite dos argumentos. Con ellos construye y devuelve un pair que tendr, en su campo first, el valor pasado en el primer argumento de la funcin make_pair() y, en el campo second, el valor pasado en el segundo argumento. As de sencillo. Siguiendo con el ejemplo, ambas formas se ven en el siguiente fragmento:
// ~~~~~~~~~~~~~~~~~~~ // La forma complicada // // Declaracin de un 'pair' compatible con lo que espera el 'multimap' typedef tipo_mapa::key_type key_type; // Sinnimo de la "clave" typedef tipo_mapa::mapped_type mapped_type; // Sinnimo del "valor mapeado" typedef std::pair< key_type, mapped_type > tipo_par; // Sinnimo del 'pair' // La "clave" del 'multimap': una fecha cualquiera key_type fecha = 131336449; // El "valor mapeado" del 'multimap' datos_sensor Reg; // Aqu obtendramos los valores que haya // enviado un sensor, almacenndolos en 'Reg'

// Declaramos un 'pair'. Es una estructura. Luego, sus campos son pblicos tipo_par mi_par; // Asignar valores a los campos del 'pair' mi_par.first = fecha; mi_par.second = Reg; // Agregar el 'pair' al contenedor 'multimap' fMapaSensor.insert(mi_par); // ~~~~~~~~~~~~~~~~~ // La forma sencilla //

40 Se encuentran referencias a pair, entre otros, en los siguientes nmeros de la Revista Sntesis: pgina 47 del N 3; pgina 100 y siguientes del N 8; pgina 90 y siguientes del N 13; pgina 101 y siguientes del N 17 (en este nmero se habla de pair de manera especial, ya que se dedica el apartado Una clase para los iteradores a hablar sobre ellos, junto a un ejemplo prctico); pgina 81 y siguientes del N 19.

Revista Sntesis - Junio de 2006

47

Contenedores asociativos (y IV)

C++

// // // // // // // // //

No hacen falta los 'typedef' anteriores ni la declaracin del 'pair'. Lo que s hay que hacer es declarar las variables que conforman la "clave" y el "valor mapeado" del 'multimap'. Y, por supuesto, darles los valores que se consideren oportunos: key_type fecha; datos_sensor Reg; ...

// Agregar el 'pair' al contenedor 'multimap' fMapaSensor.insert(std::make_pair(fecha, Reg));

En la forma en que hemos utilizado insert(), el iterador devuelto no es muy til, en general. Como se ha dicho, apunta al elemento agregado, as que podramos hacer algo con l. Pero, si se hubiese deseado hacer algo (por ejemplo, cambiar los valores de 'Reg'), podra haberse hecho antes de agregar el elemento. La funcin insert() tiene otras formas (sobrecarga) que permiten copiar un rango desde otro contenedor al multimap o indicar el punto donde debe comenzar la bsqueda para insertar el nuevo elemento:
class multimap { public: // Agregar un rango de elementos, entre '[first, last[' (no se incluye 'last') template< class InputIterator > void insert(InputIterator first, InputIterator last); // Agregar el elemento 'x' al contenedor, comenzando la bsqueda en 'position' iterator insert(iterator position, const value_type& x); };

Esta ltima forma es til cuando se conocen los datos contenidos y, adems, se tiene un iterador al elemento que debe ir antes del que se pretende insertar. En cualquier otro caso, especificar un punto del contenedor a insert(), puede hacer que la insercin de elementos tarde ms de lo esperado ya que, si no se encuentra el lugar de insercin entre el punto que se indique y el final del contenedor, se volver al inicio y a recorrer todo el contenedor, hasta encontrar su lugar de insercin. En el cdigo de ejemplo que acompaa a este artculo, los datos del valor mapeado del multimap se obtienen de manera aleatoria, utilizando la funcin rand(), de la biblioteca estndar. El momento de la insercin se ha colocado en el constructor del formulario denominado TFrmTomasSensor (fichero "SensoFRM.cpp"). Se han dejado varios comentarios en el cdigo para que puedan hacerse diversas simulaciones, sin ms que quitar los comentarios o cambiar los valores declarados en l.

Probando el contenedor
Dado que ya tenemos algo en el contenedor, el siguiente paso que se desear dar ser, probablemente, verlo en accin. Es decir, averiguar si se ha declarado correctamente para que cumpla con su cometido. Para los propsitos del ejemplo que estamos siguiendo, el valor mapeado no es interesante. De momento, al menos. Lo importante, a estas alturas, es que el contenedor tenga los elementos ordenados por fecha. Esto puede darse por supuesto ya que, como se sabe, los contenedores asociativos mantienen sus elementos ordenados automticamente por la clave. La duda puede estar en si se ha declarado bien dicha clave. As que, para despejar cualquier duda, haremos una prueba, trasladando el contenido de la clave del multimap a un rbol similar al que se muestra en la Figura 3. Si los elementos se han ordenado como se espera, aparecer un rbol como el que se ve en la imagen. Recordemos que los datos se han agregado de manera aleatoria (mediante rand(), como se ha dicho) pero que, de provenir de muestras reales, tampoco tendramos la seguridad de que estuviesen ordenados, antes de insertarlos en el contenedor.

Revista Sntesis - Junio de 2006

48

Contenedores asociativos (y IV)

C++

Rellenar un rbol como el de la Figura 3 partiendo de un contenedor asociativo es una tarea sencilla. Ms an cuando se sabe el nmero de niveles que se van a mostrar en el rbol: basta con recorrer el contenedor desde su inicio (begin()) hasta el fin (end()), agregando cada elemento que interese al rbol. Y es que hay casos, como el que nos ocupa, en que no todos los elementos del contenedor son tiles en un contexto dado. En este ejemplo tenemos un elemento por cada toma que haya realizado cada sensor, lo que supone que puede haber varios registros por segundo, algunos ms por minuto, ms an por hora, etc. Pero el rbol ha de mostrar slo hasta el nivel de los das. As que, en el recorrido por el contenedor para construir el rbol, habr que saltarse aquellos registros que estn duplicados, tanto para los das, como para los meses y los aos. Eso es justamente lo que hace el fragmento de cdigo siguiente:
// El cdigo completo de esta funcin est en "SensoFRM.cpp" void __fastcall TFrmTomasSensor::rellenar_arbol(void) { // Niveles a mostrar en el rbol typedef enum etiq_niveles { // Este enumerado est en el fichero "SensoFRM.h" NIVEL_RAIZ, NIVEL_ANO, NIVEL_MES, NIVEL_DIA, NIVEL_TOTAL } tipo_nivel; // Necesitamos un puntero a un nodo por cada nivel en el rbol Comctrls::TTreeNode *pNodo[NIVEL_TOTAL]; // La raz del rbol pNodo[NIVEL_RAIZ] = ArbolDatos->Items->AddObject ( 0, "Datos de los sensores", reinterpret_cast<System::TObject*>(NIVEL_RAIZ) ); // ~~~~ // Aos // ~~~~ // Inicio del contenedor const_iterator c_iter = fMapaSensor.begin(); while (c_iter != fMapaSensor.end()) { datos_fecha d(c_iter->first); // Objeto temporal para el manejo de fechas // Ao actual const std::time_t uAno = d.get_ano(); // Dar formato al texto del nodo char cTexto[10]; std::sprintf(cTexto, "Ao %u", uAno); // Agregar el nodo del ao pNodo[NIVEL_ANO] = ArbolDatos->Items->AddChildObject ( pNodo[NIVEL_RAIZ], cTexto, reinterpret_cast<System::TObject*>(c_iter->first) ); // ~~~~~ // Meses

Figura 3.- Una vista de la clave

Revista Sntesis - Junio de 2006

49

Contenedores asociativos (y IV)

C++

// ~~~~~ // Recorrido por los meses del ao actual while ((c_iter != fMapaSensor.end()) && (uAno == d.get_ano())) { // Mes actual const tipo_mes uMes = d.get_mes(); if ((Ningun_mes < uMes) && (uMes < Total_meses)) { // Agregar el nodo del mes pNodo[NIVEL_MES] = ArbolDatos->Items->AddChildObject ( pNodo[NIVEL_ANO], fNombreMes[uMes], // Esto es un array con los nombres de los meses reinterpret_cast<System::TObject*>(c_iter->first) ); // ~~~~ // Das // ~~~~ // Recorrido por los das del ao y mes actual std::time_t uPreDia = 0; while ((c_iter != fMapaSensor.end()) && (uAno == d.asignar_fecha(c_iter->first).get_ano()) && (uMes == d.get_mes())) { // Da actual const std::time_t uDia = d.get_dia(); if (uDia != uPreDia) { uPreDia = uDia; // Texto para el nodo std::sprintf(cTexto, "Da %u", uDia); // Agregar el nodo del da pNodo[NIVEL_DIA] = ArbolDatos->Items->AddChildObject ( pNodo[NIVEL_MES], cTexto, reinterpret_cast<System::TObject*>(c_iter->first) ); } ++c_iter; // Siguiente da } } } } }

El filtro que elimina registros duplicados se encuentra en la lnea 'if (uDia != uPreDia)', en el tercer bucle interior: si el da que valoramos (uDia) es igual que el del registro anterior (uPreDia), no se agrega el nodo al rbol. El resto del cdigo no tiene ningn misterio ya que es un bucle (bueno, tres) tpico sobre los elementos de un contenedor. En este caso, se trata de un contenedor asociativo. Pero un bucle similar a ese hemos utilizado en otras ocasiones41 para recorrer diversos tipos de contenedores. En cuanto a los nodos del rbol, se identificarn mediante dos propiedades. Por un lado, el nivel del nodo nos dir cul ha seleccionado el usuario. Este nivel corresponde a la propiedad Level de cualquier elemento de tipo TTreeNode de un componente TTreeView de la VCL. Por otro lado, la propiedad Data del TTreeNode contendr la clave que corresponde al nodo. En los bucles anteriores, el valor de Data se ha agregado mediante el tercer argumento del procedimiento AddChildObject(). As, el valor de Level nos dir si es un da, mes o ao lo que el

41 Como ejemplo de un bucle while similar, en la pgina 71 del N 18 de la Revista Sntesis, aparece un ejemplo en el que se utiliza ese tipo de bucle para agregar elementos desde un set de string a un TListBox.

Revista Sntesis - Junio de 2006

50

Contenedores asociativos (y IV)

C++

usuario ha seleccionado en el rbol. Obteniendo el Data del nodo sabremos qu da, mes o ao es el seleccionado, ya que Data contiene la clave del multimap. A resaltar que, en todo el cdigo de relleno del rbol, no aparece el elemento second (valor mapeado) del iterador (que, como se ve, es un pair), devuelto por la funcin begin() del contenedor. Utilizamos exclusivamente el campo first del pair, que contiene la clave del contenedor.

Bsqueda y lmites
Una vez se tienen los datos en el multimap lo normal es que se necesiten consultar o, incluso, modificar. En este ejemplo, por sus caractersticas de toma de datos externos, no se har modificacin alguna. La bsqueda ms sencilla es la que proporciona la funcin find()42 del contenedor. El uso y el resultado de esta funcin es idntico al que se obtiene al aplicarla sobre un map, un set43 o un multiset: dado un valor de clave, devuelve un iterador (un pair) al elemento, si hay alguno en el contenedor; en caso contrario, el iterador devuelto apuntar ms all del final del contenedor, esto es, valdr end(). Sin embargo, en un multimap o en un multiset tiene menos sentido utilizar find() que en un map o un set. La razn es que el iterador devuelto por find(), en un contenedor de clave nica (map o set), apuntar al elemento de la clave a buscar o, si no existe, al final del contenedor, end(). Al hacer la bsqueda con find() en un contenedor de clave mltiple (multimap o multiset), si se obtiene end() se sabr que el elemento no existe en el Figura 4.- Bsquedas en los contenedores de clave mltiple contenedor (probablemente este sea su cometido ms apropiado) pero, para cualquier otro valor, sabremos que existe un elemento, al menos, con la clave buscada. Lo que no podremos saber con find() es cuntos elementos existen con esa clave ni dnde acaba el rango de elementos de la misma clave ya que, como se ha dicho, devuelve el primer elemento que encuentre con la clave solicitada. Por ello, para las bsquedas en los contenedores de clave mltiple ( multimap y multiset), se utilizan tres funciones especialmente diseadas44 para proporcionar un rango de elementos que tengan el mismo valor en la clave a buscar (ver la Figura 4):
42 Sobre la familia de funciones find se trat en el artculo Algoritmos I, del N 7 de la Revista Sntesis. 43 El uso de la funcin find() se ilustra en el apartado Operaciones en el contenedor", pgina 109 y siguientes del N 17 de la Revista Sntesis. Tanto esa funcin de bsqueda como las que veremos a continuacin se basan en la homnimas del rbol binario (__rb_tree o _Rb_tree), subyacente a todos los contenedores asociativos. En la serie sobre rboles binarios, entre el N 14 y 16 de la Revista Sntesis, se explic el fundamento de ese tipo de rboles. 44 La biblioteca estndar proporciona tres funciones de plantilla, independientes de cualquier contenedor, que tienen tanto el mismo nombre como el mismo efecto que las que se van a mencionar. Estas funciones se trataron en el artculo Algoritmos VII. Resumen, del N 13 de la Revista Sntesis, pgina 79 y siguientes. Se pueden utilizar las funciones independientes o las que proporciona el contenedor (multimap, multiset, map o set), aunque las del contenedor estn optimizadas para tratar con el tipo de elemento contenido.

Revista Sntesis - Junio de 2006

51

Contenedores asociativos (y IV)

C++

lower_bound(x). Realiza una bsqueda del elemento de clave x. Si se encuentra, devuelve un iterador que apunta al primer elemento que contenga ese valor. Si no se encuentra, devuelve un iterador que apunta al primer elemento mayor que x. El trmino primer elemento se refiere a la disposicin de stos en el contenedor. As que, si hay 2 elementos con la misma clave, al incrementar el iterador devuelto por esta funcin, estaremos en el segundo elemento. Por lo mismo, si se decrementa el iterador devuelto (como es lgico, siempre que no estemos en el inicio del contenedor o begin()), nos situaremos en un elemento que tiene una clave menor que la pedida. El iterador devuelto puede ser end() si x tiene un valor mayor que el elemento mayor de la secuencia. Es sencillo averiguar por qu trabaja as esta funcin: va pasando por la secuencia, desde el begin() al end(), buscando un elemento mayor o igual; cuando encuentra uno igual, lo devuelve; cuando encuentra un elemento mayor (o se topa con el end() de la secuencia), sabe que no existe, finaliza la bsqueda y devuelve ese elemento. La funcin lower_bound() devuelve, pues, el lmite inferior del valor de clave pasado en su argumento. upper_bound(x). Realiza una bsqueda del elemento de clave x. Si se encuentra, devuelve el siguiente elemento mayor que x. Si no se encuentra, devuelve end(). Tambin devolver end() si el elemento x es el mayor del contenedor. El resultado es un iterador que apunta un lugar ms all del elemento solicitado: el lmite superior del valor de clave pasado en su argumento. equal_range(x). Como hacer una bsqueda para encontrar los lmites inferior y superior de un rango es una operacin habitual en los contenedores asociativos de clave mltiple (multimap y multiset), stos proporcionan la funcin equal_range() que devuelve, en una sola llamada, los elementos del contenedor que tengan una determinada clave x. La funcin equal_range() devuelve un pair de iteradores. El campo first del pair contiene un iterador que apunta al elemento de clave mayor o igual que el solicitado. El campo second del pair contiene un iterador que apunta al elemento de clave mayor que el solicitado. Dicho con otras palabras: una llamada a la funcin equal_range(x) es una forma abreviada de llamar, primero, a lower_bound(x) y almacenar su retorno en el campo first del pair para, a continuacin, llamar a la funcin upper_bound(x) y almacenar su retorno en el campo second del pair. As pues, el pair devuelto contiene los lmites del contenedor, inferior y superior, en que se encuentran los elementos de clave x.

Ahora, armados con estas funciones, podemos volver al ejemplo propuesto. Ya hemos rellenado el contenedor multimap y hemos hecho que su clave se muestre en un TTreeView, donde podemos identificar el nodo que el usuario ha seleccionado gracias a las propiedades Level y Data del TTreeNode. Cuando el usuario cambie de nodo, en el TTreeView se producir el evento OnChange, donde recuperamos ambos valores de la siguiente manera:
// El cdigo completo de esta funcin est en el fichero "SensoFRM.cpp" void __fastcall TFrmTomasSensor::ArbolDatosChange(TObject *Sender, TTreeNode *Node) { switch (const tipo_nivel eNivel = static_cast< tipo_nivel >(Node->Level)) { case NIVEL_ANO: case NIVEL_MES: case NIVEL_DIA: { // Fecha almacenada en el nodo const std::time_t fecha = reinterpret_cast< std::time_t >(Node->Data); // ... Ahora vamos con esto break; } default: { // Este es el nodo raz del rbol, ya que slo hay 4 niveles. // ... mostrar la pgina del nodo raz (no hay datos asociados) break; } } }

Revista Sntesis - Junio de 2006

52

Contenedores asociativos (y IV)

C++

Se espera que exista una correspondencia o coordinacin, entre el nodo seleccionado y el resto de los datos que muestra el formulario, similar a lo indicado en la Figura 5. Dado que, con el cdigo anterior, ya tenemos identificados los datos que necesitamos mostrar, ahora slo resta encontrar esos datos en el contenedor. Podramos recorrer el mismo, desde su inicio a su fin, comprobando el valor de la clave de cada elemento con el Figura 5.- Coordinacin entre el nodo seleccionado y el resto de los datos que hemos obtenido de Data y almacenado en la variable fecha. Sin embargo, es mucho ms sencillo utilizar las funciones que hemos presentado antes para delimitar, con los valores que devuelven, el rango de los datos a mostrar en el formulario. Esto mismo es lo que hace la siguiente funcin:
// Sinnimo para el resultado de una bsqueda (declarado en "SensoFRM.h") typedef std::pair< const_iterator, const_iterator > tipo_rango_const; // El cdigo completo de esta funcin est en el fichero "SensoFRM.cpp" TFrmTomasSensor::tipo_rango_const __fastcall TFrmTomasSensor::obtener_rango(tipo_nivel eNivel, std::time_t fecha) const { switch (eNivel) { case NIVEL_DIA: { // Aqu se obtiene el lmite inferior y superior a la vez: // se trata del rango que abarca los registros del da actual return fMapaSensor.equal_range(fecha); } default: { // Crear un 'pair' invlido: ambos elementos apuntan a 'end()' tipo_rango_const mc_iter = std::make_pair(fMapaSensor.end(), fMapaSensor.end()); // Almacenar la fecha pasada datos_fecha d(fecha); // Discriminar, segn el nivel pasado switch (eNivel) { case NIVEL_ANO: { // Poner el 1 de enero del ao que tenga la fecha d.a_inicio_de_ano(); // ~~~~~~~~~~~~~~~ // Lmite inferior // Corresponder a 'begin()' si 'd' es igual al primer registro.

Revista Sntesis - Junio de 2006

53

Contenedores asociativos (y IV)

C++

// Corresponder al 1 de enero del ao de 'fecha', si existe. // Y, si no existe, al siguiente registro mayor que el 1 de enero. mc_iter.first = fMapaSensor.lower_bound(d); // Pasar al 31 de diciembre del ao que tenga la fecha d.a_fin_de_ano(); // ~~~~~~~~~~~~~~~ // Lmite superior // Corresponder a 'end()' si 'd' es mayor o igual al ltimo registro. // Corresponder al 1 de enero del ao siguiente, si existe. // Y, si no existe, al siguiente registro, mayor que // el da 1 de enero del ao siguiente. mc_iter.second = fMapaSensor.upper_bound(d); break; } case NIVEL_MES: { // Ponerse en el primer da del mes actual d.a_inicio_de_mes(); // ~~~~~~~~~~~~~~~ // Lmite inferior // Corresponder a 'begin()' si 'd' es igual al primer registro. // Corresponder al da 1 del mes de 'fecha', si existe. // Y, si no existe, al siguiente registro mayor que el da 1 del mes. mc_iter.first = fMapaSensor.lower_bound(d); // Ponerse en el ltimo da del mes actual d.a_fin_de_mes(); // ~~~~~~~~~~~~~~~ // Lmite superior // Corresponder a 'end()' si 'd' es mayor o igual al ltimo registro. // Corresponder al da 1 del mes siguiente, si existe. // Y, si no existe, al siguiente registro, mayor que // el da 1 del mes siguiente. mc_iter.second = fMapaSensor.upper_bound(d); break; } } return mc_iter; // Devuelve el 'pair' de iteradores hallado } } }

La funcin anterior devuelve un pair de pair, ya que los iteradores del contenedor son, a su vez pair: el primer elemento, first, es la clave y el segundo, second, el valor mapeado. De ah que las asignaciones, dentro de la funcin, se hagan a uno u otro (first o second) campo del pair. Se ha escogido que sean constantes (ver el typedef colocado antes de la funcin) porque no se van a modificar los valores de los iteradores devueltos. Por un motivo similar, la funcin se declara const: es una funcin de consulta, que no afecta al multimap ni a ningn otro elemento de la clase (formulario, en este caso) en que est definida. El rango de elementos que corresponden a un da son todos aquellos que tengan por clave el valor pasado en el argumento fecha de la funcin. Dado este valor, la funcin equal_range() se encarga de hallar los lmites del contenedor: el primer elemento igual o mayor al valor de clave pasado en fecha, que copiar en el campo first del pair devuelto, y el elemento de clave mayor a dicho valor, que copiar en el elemento second del pair. Notar que el valor de retorno de equal_range() es el valor de retorno de la funcin. Es decir, se ha escogido un tipo de retorno igual al de equal_range() para obtener un acceso uniforme a la funcin, sea cual sea el rango solicitado. Por ello hay que hacer un poco de trabajo extra cuando el nivel no es NIVEL_DIA.

Revista Sntesis - Junio de 2006

54

Contenedores asociativos (y IV)

C++

El rango de elementos que corresponden a un ao (NIVEL_ANO) son todos aquellos que vayan entre el da 1 de enero y el 31 de diciembre de ese ao. Las funciones a_inicio_de_ano() y a_fin_de_ano(), de la clase datos_fecha, se encargan de fijar esos das (el 1 de enero y el 31 de diciembre, respectivamente). Si tenemos una fecha igual al da 1 de enero, la funcin equal_range() devolver, como antes, los lmites del contenedor donde la clave coincida con el 1 de enero. Sin embargo, cuando estamos intentando averiguar los lmites de un ao, de lo que devuelve equal_range() slo nos interesar el primer lmite. Por ejemplo, equal_range("1/01/2004") devuelve, en first, el primer registro que coincida con el 1 de enero y, en second, el primer registro del 2 de enero (suponiendo que existan, claro est). La primera parte es correcta pero la segunda no es la que se desea: como se quiere todo el ao, para abarcarlo completamente, se requiere el registro que vaya tras el 31 de diciembre. Adems, si slo interesase la primera parte y se estuviese seguro de su existencia, la funcin find() sera mucho ms apropiada, aunque slo fuese porque es ms rpida, al buscar slo un lmite y no dos, como hace equal_range(). Para fijar los lmites de un ao, en el cdigo de NIVEL_ANO se realizan dos llamadas. La primera, tras asignar la fecha al da 1 de enero con la funcin a_inicio_de_ano(), de la clase datos_fecha, utiliza la funcin lower_bound() para encontrar el primer registro que corresponda a esa fecha o sea superior a ella. Hay que resaltar que la funcin find() no servira aqu: si find() no encuentra la clave, devuelve end(). Por ejemplo, si la primera fecha fuese del da 2, find() no la encontrara cuando se le pasa el da 1. El valor devuelto por lower_bound() se almacena en el campo first del pair de iteradores a devolver y corresponde, pues, al primer registro del ao que contenga el argumento fecha de la funcin: es el lmite inferior del rango. Para hallar el lmite superior del rango se sigue un mtodo similar. Primero, se asigna la fecha del 31 de diciembre del ao en curso, mediante una llamada a la funcin a_fin_de_ano(), de la clase datos_fecha, para, a continuacin, utilizar la funcin upper_bound() del contenedor. Esta funcin, como se ha dicho, devolver un iterador al elemento mayor que el que se le pide. As que, dada la fecha del 31 de diciembre, devolver el primer registro del da 1 de enero del siguiente ao, si existe. Y, sino, el del 2, o el del 3, o... el end(). En el NIVEL_MES el mtodo seguido es el mismo, excepto porque aqu se utilizan las funciones a_inicio_de_mes() y a_fin_de_mes(), de la clase datos_fecha, para fijar la fecha al da 1 del mes y al final del mes: da 28, 29, 30 o 31, segn el mes que sea y el ao, bisiesto o no. A resaltar que, tanto para el NIVEL_ANO como para el NIVEL_MES, el dato devuelto por upper_bound() slo sirve de lmite, esto es, slo se tiene en cuenta para saber dnde finalizar el recorrido (como si fuese el end() del contenedor). As que no importa si ese valor no corresponde con el mes o ao pedido: es el elemento final del rango. Con los lmites obtenidos, se puede hacer ya cualquier operacin sobre los valores mapeados en el rango que corresponda al nodo seleccionado en el TTreeView. Por ejemplo, mediante un bucle como el siguiente 45 conseguiramos hallar las medias de un mes concreto, que son los datos que aparecen en los controles que se ven bajo la tabla de la Figura 5:
// Establecer la fecha al 2/09/2004 datos_fecha d = datos_fecha().set_dia(__2).set_mes(Septiembre).set_ano(2004); // Obtener el rango inicial y final para el mes actual tipo_rango_const mc_iter = obtener_rango(NIVEL_MES, d); // Tenemos algn registro en la fecha pedida? if (mc_iter.first != mc_iter.second) { // Iniciar los valores float KmHora = 0.0; // Km/h float Decibelios = 0.0; // Decibelios unsigned eTotal = 0; // Nmero de registros implicados en los clculos // Mientras estemos en el rango (mes)... while (mc_iter.first != mc_iter.second)

45 El cdigo es el de la funcin total_en_mes() que se encuentra en el fichero "SensoFRM.cpp", simplificado.

Revista Sntesis - Junio de 2006

55

Contenedores asociativos (y IV)

C++

{ KmHora += mc_iter.first->second.fKmHora; Decibelios += mc_iter.first->second.fDecibelios; ++eTotal; ++mc_iter.first; } // Obtener las medias if (0 < eTotal) { KmHora /= eTotal; Decibelios /= eTotal; } } // Nmero de registros en el clculo // Siguiente registro del mes

Cdigo similar al anterior, que se ocupa slo del mes, sirve para calcular las medias en un da o ao concreto. Con la ayuda de esas dos funciones, el evento OnChange del TTreeView se simplifica:
// El cdigo completo de esta funcin est en el fichero "SensoFRM.cpp" void __fastcall TFrmTomasSensor::ArbolDatosChange(TObject *Sender, TTreeNode *Node) { switch (const tipo_nivel eNivel = static_cast< tipo_nivel >(Node->Level)) { case NIVEL_ANO: case NIVEL_MES: case NIVEL_DIA: { // Fecha almacenada en el nodo const std::time_t fecha = reinterpret_cast< std::time_t >(Node->Data); // Nmero de filas a mostrar en la tabla size_type uFilas = FILA_CABECERA; // Almacena el nmero de registros asociados al nodo 'Node' size_type uTamano = 0; // Velocidad media de los vehculos, en km/h float eKmHora = 0.0; // Intensidad del sonido media, en decibelios float eDecibelios = 0.0; // Discriminar, segn nivel switch (eNivel) { case NIVEL_ANO: { // Obtener los datos de los meses del ao actual for (unsigned uMes = Enero; uMes < Total_meses; ++uMes) { datos_sensor Reg; // Registro para obtener las medias del mes size_type eTotal; // Nmero de registros involucrados en los clculos if (total_en_mes(fecha, uMes, &Reg, &eTotal)) { eKmHora += Reg.fKmHora; eDecibelios += Reg.fDecibelios; uTamano += eTotal; ++uFilas; } } break; } // ... De manera similar para el mes y el da }

Revista Sntesis - Junio de 2006

56

Contenedores asociativos (y IV)

C++

break; } } }

Los datos obtenidos en el bucle del fragmento anterior pueden mostrarse en el formulario. Son, precisamente, los que aparecen en la parte inferior derecha de la Figura 5, es decir, los resmenes o medias del nodo seleccionado: nmero de registros a mostrar en la tabla y nmero de registros que corresponden al nodo seleccionado, velocidad media e intensidad del sonido media.

Declaracin del multiset


Cuando el usuario selecciona un nodo que corresponde al nivel de un ao o de un mes, los datos que muestra el formulario son correctos. Si el nivel es un ao, se realiza la media, en km/h y db, para los meses de ese ao. Si el nivel es un mes, se realiza la media, en km/h y db, para los das de ese mes. El rango de registros se muestra, entonces, en la tabla (ver Figura 5) o en el grfico. Para un nodo que corresponda a un da, los datos a mostrar en el formulario deben ser los de las horas, minutos y segundos de ese da. Y, segn lo planteado en los requisitos del ejemplo, los datos deben poderse agrupar a diversos niveles de detalle: por horas, por minutos, por segundos o mostrar todos los registros. Para permitir al usuario establecer ese nivel de detalle se ha colocado un cuadro de grupo, al que se ha etiquetado como Detalle, tal y como Figura 6.- Filtros a aplicar cuando se selecciona un da se muestra en la Figura 6. Podemos obtener los registros del multimap que correspondan al da seleccionado siguiendo el mtodo que se ha explicado en el apartado anterior. Sin embargo, como en la clave del multimap no se encuentran los datos de la hora, minutos y segundos, los registros obtenidos de esa manera no estarn ordenados. Es decir, el primer registro del da que el usuario ha seleccionado en el TTreeView puede corresponder a las 23:00 horas y el siguiente a las 3:00 horas, aunque ambos estn dentro del mismo da. Es de esperar que el usuario va a querer ver los registros en algn orden. Y vamos a suponer que los quiere ver en orden ascendente de hora, minutos y segundos46. Hay varias maneras de ordenar los datos del multimap por un determinado campo. El mtodo ms sencillo hubiese sido hacer que la clave del multimap contuviese la hora, minutos y segundos. En ese caso, habra que haber definido el operador menor que sobre los datos de la clave. Tras ello, el rango obtenido mediante la funcin obtener_rango(), presentada en el apartado anterior, hubiese estado ordenado ya.

46 Para formar un filtro sobre cualquier otro dato (km/h, db o sensor), el proceso es similar al que se explicar a continuacin, excepto porque habr que hacer las operaciones oportunas con el dato por el que se quiera ordenar.

Revista Sntesis - Junio de 2006

57

Contenedores asociativos (y IV)

C++

Como no ha sido as, vamos a necesitar ordenarlos ahora. Un mtodo, nada recomendado, consistira en pasar por el contenedor, desde su begin() a su end(), y ordenar los datos del valor asociado, dado que es ah donde se encuentran los datos que nos interesa ordenar ahora. A resaltar que en esta ordenacin no se tocara la clave del contenedor, sino los datos asociados a la clave (el valor mapeado): al pasar por el contenedor, si un valor mapeado tiene una hora posterior al otro, se intercambiaran ambos valores. El mtodo, aunque no es difcil de realizar, no es muy recomendable porque habra demasiado trasiego de datos dentro del contenedor: pensemos en que puede haber miles o cientos de miles de elementos en el multimap. Un mtodo mejor consistira en tener una especie de contenedor auxiliar que, dado un rango de elementos del multimap, ordenase los datos de los valores asociados. Y, como deseamos que el ordenamiento sea automtico, debemos inclinarnos hacia un contenedor asociativo donde, como sabemos, el ordenamiento est incorporado (menor que, por defecto). El contenedor auxiliar debe trabajar, pues, con los valores mapeados del multimap. Aunque hemos escogido un valor asociado pequeo (sizeof(datos_sensor) == 12 bytes, en nuestro ejemplo), vamos a suponer que no es as. Adems, es posible que, en el futuro, se agreguen ms campos a datos_sensor, haciendo que su tamao no sea tan pequeo. Por otro lado, los datos obtenidos de cada sensor ya los tenemos en el multimap, por lo que no es necesario replicarlos en este contenedor auxiliar: bastar con que sus elementos apunten al valor contenido en el multimap. Es decir, el contenedor auxiliar ser de punteros a los valores asociados en el multimap, esto es, de punteros a estructuras de tipo datos_sensor. Como se sabe, los datos contenidos en un datos_sensor no son nicos, ya que puede haber dos o ms registros con los mismos valores de hora, minuto, segundo, velocidad,... As que, por un lado, falta algo que pueda actuar de clave. Por otro lado, ni siquiera tomando a toda la estructura datos_sensor como clave del contenedor asociativo tendramos una clave nica. Y, an as, necesitaremos tener ordenados los datos por hora, minutos y segundos. Por ello necesitamos un contenedor asociativo de clave mltiple, esto es, un contenedor que admita valores repetidos en su clave y que, adems, permita ordenar cmodamente los elementos contenidos. Dadas esas caractersticas, el contenedor a elegir es multiset47. La ventaja de utilizar un contenedor auxiliar (multiset, en este caso) frente a haber definido el criterio de orden en la clave del multimap es que podemos cambiar el orden en que se muestran los datos en el formulario sin ms que cambiar de contenedor auxiliar. Algo tpico es que pulsen sobre el encabezado de una de las columnas de la tabla (ver Figura 6) y que los datos se ordenen por esa columna. En casos as, si el criterio de orden est dentro del contenedor que posee los datos, ser difcil mostrarlos ordenados. Es claro que siempre pueden extraerse los datos, ordenarlos y mostrarlos en la tabla. Sin embargo, en mi opinin, el mtodo que se presenta aqu es ms sencillo. Definimos un multiset de punteros al valor mapeado del multimap de la siguiente manera:

// Estructura con los datos tomados por el sensor struct datos_sensor { /* ... como antes... */ }; // Sinnimo de un conjunto de punteros a 'datos_sensor' typedef std::multiset < const datos_sensor*, // Clave: es un puntero nm_util::comparar_ptr< datos_sensor > // Mtodo de comparacin > tipo_conjunto; // Un 'multiset' que almacena un puntero al "valor mapeado" en el 'multimap' tipo_conjunto fConjuntoSensor;

47 Hay otras alternativas a multiset pero, en este caso, ese contenedor es el que me parece ms apropiado.

Revista Sntesis - Junio de 2006

58

Contenedores asociativos (y IV)

C++

El mtodo de comparacin en el multiset, comparar_ptr, es el mismo que el que utilizamos en el artculo anterior48, excepto porque se ha trasladado a un fichero aparte ("ComparaP.h") y se ha enmarcado en el espacio de nombres denominado nm_util. La plantilla de clase comparar_ptr se limita a desreferenciar los punteros que recibe su operador de llamada a funcin (operator()) y a aplicarles el operador menor que (ver la referencia de la nota 48). As que, para que la cosa funcione como se espera y, sobretodo, para que compile, se necesita definir lo que supone menor que en la clase que contiene el multiset. Es algo que se ya ha hecho en los ejemplos de varios artculos anteriores49, pero que repetimos aqu por su simplicidad:
// Estructura con los datos tomados por el sensor struct datos_sensor { // ... // Operador "menor que" bool operator<(const datos_sensor &R) const { if (fHora == R.fHora) // Para horas iguales... { return ( (fMinutos == R.fMinutos) ? (fSegundos <= R.fSegundos) // ..., en el mismo minuto, comparar segundos : (fMinutos < R.fMinutos) // ..., en distintos minutos, compararlos ); } return (fHora < R.fHora); // Para distintas horas } };

Una vez definido el operador menor que sobre los elementos que contiene el multiset, cualquier dato que se aada al mismo quedar ordenado tal y como diga que ha de ordenarse dicho operador. A resaltar que se tiene absoluta libertad para definir lo que es menor que en cualquier tipo de dato, sin ms que especificar, en la declaracin del multiset (en realidad, de cualquier contenedor asociativo), el mtodo de comparacin a utilizar. En este ejemplo se ha indicado la clase comparar_ptr como mtodo de comparacin. Una clase similar podra hacer que los registros de otro multiset, an conteniendo los mismos datos, se ordenasen por otro campo o campos (por ejemplo, por km/h o por decibelios o por ambos).

Agregar elementos al multiset


Para agregar elementos al contenedor multiset se utiliza el mismo mtodo que con multimap y con set, esto es, la funcin insert() del contenedor. La diferencia con multimap es que, lo que recibe insert(), no es un pair sino la propia clave. La razn es que en un multiset, a diferencia de un multimap, no hay valor asociado. As que lo nico que puede agregarse es la clave. En el ejemplo que estamos poniendo, el multiset es de punteros. As que, excepto por la desreferencia (&) que se muestra en el ejemplo siguiente50, el cdigo es similar para cualquier multiset:
// Obtener el rango inicial y final para el "nivel da" y la fecha 'fecha' tipo_rango_const mc_iter = obtener_rango(NIVEL_DIA, fecha);

48 Ver la pgina 79 del N 19 de la Revista Sntesis. 49 El operador menor que, definido para una estructura, se muestra en la pgina 78 del N 19 de la Revista Sntesis. 50 El cdigo es el de la funcin rellenar_conjunto(), del fichero "SensoFRM.cpp", simplificado.

Revista Sntesis - Junio de 2006

59

Contenedores asociativos (y IV)

C++

// Vaciar el conjunto fConjuntoSensor.clear(); // Suma de la velocidad de los vehculos, para // todos los registros de la fecha, en km/hora float eKmHoraE = 0.0; // Suma de la intensidad del sonido, para todos // los registros de la fecha, en decibelios float eDecibeliosE = 0.0; // Recorrer el rango correspondiente al da while (mc_iter.first != mc_iter.second) { // Agregar puntero al 'multiset' fConjuntoSensor.insert(&mc_iter.first->second); // Acumular km/h y decibelios eKmHoraE += mc_iter.first->second.fKmHora; eDecibeliosE += mc_iter.first->second.fDecibelios; // Siguiente registro del 'multimap' ++mc_iter.first; }

Las sumas que se realizan dentro del bucle while se utilizan, posteriormente, para mostrar las medias globales en los controles de edicin que se muestran, abajo, en la Figura 6. No tienen ningn efecto sobre la insercin de datos en el multiset (la llamada a la funcin insert(), digo). Si las he dejado ha sido para que se vea cmo se acceden a los campos del pair de pair devuelto por la funcin obtener_rango(), presentada antes, que, como se ha dicho, devuelve lo mismo que la funcin equal_range() del contenedor asociativo (multimap, en este caso). En el ejemplo, mc_iter es un pair que contiene el rango inferior (campo first, que es un pair a su vez) y superior (campo second, otro pair) de los elementos contenidos en el multimap que coinciden con el da pedido. Por su parte, mc_iter.first es un iterador por los elementos del multimap. Por lo tanto, es un pair. El campo first de este pair (mc_iter.first->first) es la clave del multimap. El campo second (mc_iter.first->second) del pair contiene el valor asociado a la clave. Este es el valor que se inserta en el multiset, mediante 'fConjuntoSensor.insert(&mc_iter.first->second)'. Se desreferencia para obtener su direccin ya que, lo que almacena el multiset, son punteros: de no utilizar &, se estara indicando un registro (struct), en vez de un puntero (a una struct, en este caso). Por el hecho de ser punteros tambin reviste un poco de complicacin la semntica utilizada para acceder a los elementos del multiset y, por ejemplo, realizar operaciones con ellos51:
// Valor imposible (MAX_HORAS == 23) unsigned char uPreHora = Reg->fHora = MAX_HORAS + 1; // Comenzar antes de la primera fila de datos (FILA_CABECERA == 0) unsigned uFila = FILA_CABECERA; // Situarse al inicio del conjunto de punteros const_iterator_set sc_iter = fConjuntoSensor.begin(); while ((sc_iter != fConjuntoSensor.end()) && (uFila < eFila)) { if ((*sc_iter)->fHora != uPreHora) { uPreHora = (*sc_iter)->fHora; ++uFila; } if (uFila < eFila) ++sc_iter; }

51 El cdigo siguiente es el de la funcin total_en_hora(), del fichero "SensoFRM.cpp", simplificado.

Revista Sntesis - Junio de 2006

60

Contenedores asociativos (y IV)

C++

El valor de sc_iter en el cdigo anterior es un iterador. El acceso a los valores que contienen los iteradores se realiza siempre mediante el operador de acceso (operator*()). Sin embargo, aqu tenemos un iterador que apunta a un puntero. En la forma '(*sc_iter)' los parntesis son necesarios. Se indica as que quiere accederse al contenido del iterador ('*sc_iter') y, una vez accedido, obtener el valor de un campo de ese puntero. Como '(*sc_iter)' es un puntero, es necesario el uso del operador de acceso a puntero a estructura (operator->). La forma utilizada es, pues, similar a la del acceso a un puntero a un puntero.

Eliminando elementos
La combinacin de contenedores asociativos que se ha mostrado (multimap y multiset) funciona bien porque en el ejemplo no se realizan eliminaciones. De existir stas, tras una eliminacin, los elementos contenidos en el multiset dejaran de ser vlidos ya que apuntan al valor mapeado del multimap, que desaparecer tras la eliminacin, junto con la clave. As que, si en una aplicacin se utiliza un mtodo similar al presentado, hay que tener especial cuidado en vaciar el multiset (mediante clear(), por ejemplo) antes de cualquier eliminacin o, alternativamente, buscar el elemento o elementos que se vayan a eliminar del multimap y hacer que desaparezcan del multiset. Aunque en el ejemplo que hemos puesto no se eliminan en ningn momento elementos individuales, por completar la explicacin, conviene saber que las eliminaciones, en todos los contenedores asociativos, se realizan de la misma manera. La funcin erase() tiene tres versiones (sobrecarga). En la primera, se le pasa un valor correspondiente a la clave del contenedor, lo busca y, si lo encuentra, lo elimina. En un map o en un set, al ser contenedores de clave nica, el elemento eliminado ser aquel que posea la clave pasada. Sin embargo, en un multimap o un multiset, el borrado a travs de una clave afectar a todos los elementos que tengan dicha clave.
// Una fecha: 13/01/2004 datos_fecha d(2004, Enero, _13); // Eliminar todos los datos del da 13/01/2004 fMapaSensor.erase(d);

A la segunda versin de la funcin erase() se le pasa un iterador no constante, que apunta al elemento a eliminar. La funcin busca el elemento, utilizando ese iterador, y, si lo encuentra, lo elimina. Hay que resaltar que, tras la eliminacin, el iterador pasado deja de ser vlido ya que, aunque tendr los mismos valores que antes de la llamada a erase(), a la vuelta esos datos no se encuentran ya en el contenedor52. Tanto en los contenedores asociativos de clave nica (map o set) como en los de clave mltiple (multimap o multiset), esta forma elimina un solo elemento del contenedor.
// Una fecha: 31/01/2004 datos_fecha d(2004, Enero, _31); // El iterador utilizado no puede ser constante typedef tipo_mapa::iterator iterator; // Buscar la fecha iterator iter_elim = fMapaSensor.find(d); // Eliminar el (primer) elemento de fecha 31/01/2004 fMapaSensor.erase(iter_elim);

52 El contenedor no modifica, tras la eliminacin, el iterador pasado a erase(). As que los campos first y second del iterador seguirn teniendo los mismos valores, excepto que alguno de ellos sea un puntero. Si es el caso, los campos del iterador apuntarn a basura. Sea como fuere, no debe utilizarse ese iterador posteriormente.

Revista Sntesis - Junio de 2006

61

Contenedores asociativos (y IV)

C++

// Alternativa, mediante doble llamada fMapaSensor.erase(fMapaSensor.find(d));

En este ejemplo se ha utilizado la funcin find() para obtener un iterador al elemento de clave d. En los contenedores asociativos la funcin find() tiene dos versiones: una devuelve un iterador constante y la otra uno no constante. La eleccin de la versin de find() que devuelve un iterador no constante se deja en manos del compilador. Esto es, no se necesita especificar a cul de las dos versiones llamar, ya que se deduce de su uso: la funcin erase() recibe un iterador no constante. La tercera versin de la funcin erase() consiste en pasar un par de iteradores no constantes a la funcin, que indican el rango de elementos a eliminar. Como se ha dicho antes, en un multimap o en un multiset, el borrado a travs de una clave (primera versin de erase()) afectar a todos los elementos que tengan dicha clave. Si se desea eliminar un elemento especfico, se ha de pasar un iterador que apunte a dicho elemento (segunda versin) o bien el rango a eliminar (tercera versin), mediante iteradores que indiquen el comienzo y final del rango.
// Una fecha: 11/01/2004 datos_fecha fecha_0(2004, Enero, _11); // Otra fecha: 12/01/2004 datos_fecha fecha_1(2004, Enero, _12); // Los iteradores utilizados no pueden ser constantes typedef tipo_mapa::iterator iterator; // Obtener el rango inicial (primer elemento de clave 'fecha_0') iterator iter_ini = fMapaSensor.lower_bound(fecha_0); // Obtener el rango final (elemento mayor que 'fecha_1') iterator iter_fin = fMapaSensor.upper_bound(fecha_1); // Eliminar todos los datos entre el da 11/01/2004 y 12/01/2004 fMapaSensor.erase(iter_ini, iter_fin);

La ltima lnea eliminar los datos de dos das (siempre que existan, claro est) ya que la funcin upper_bound() devuelve un iterador al elemento de clave mayor que el pasado en su argumento. Si se quiere vaciar todo el contenedor se puede utilizar la funcin clear() o una forma de erase() que incluya a todos los registros. Por ejemplo, las dos lneas siguientes son equivalentes:
// Dos formas similares de vaciar el contenedor fMapaSensor.clear(); fMapaSensor.erase(fMapaSensor.begin(), fMapaSensor.end());

La llamada a clear() de la primera lnea es la forma abreviada de llamar a erase() en la segunda lnea. Es decir, clear() utiliza una llamada a erase(), pasando todo el rango de elementos del contenedor. As que, utilizar una u otra forma de vaciar el contenedor, es cuestin de gustos. Cuando se elige un contenedor asociativo de clave mltiple es porque se desean tener en l elementos con la misma clave. Sin embargo, en alguna ocasin quiz se necesiten eliminar los elementos duplicados, dejando valores nicos en el contenedor y quedando ste, por tanto, como si fuese un contenedor asociativo de clave nica. Una forma de hacerlo es copiar los elementos desde el contenedor de clave mltiple a uno equivalente (si es un multimap a un map; si es un multiset, a un set) de clave nica.

Revista Sntesis - Junio de 2006

62

Contenedores asociativos (y IV)

C++

// Sinnimo para el contenedor 'multimap': clave de tipo // 'std::time_t' y valor asociado de tipo 'datos_sensor' typedef std::multimap< std::time_t, datos_sensor > tipo_mmap; // Variable de tipo 'multimap' tipo_mmap fMapaSensor; // ... rellenar el 'multimap'... // Sinnimo para el contenedor 'map': clave de tipo // 'std::time_t' y valor asociado de tipo 'datos_sensor' typedef std::map< std::time_t, datos_sensor > tipo_map; // Variable de tipo 'map' tipo_map fCopiaMapa; // Rellenar el 'map' con los datos (nicos) del 'multimap' fCopiaMapa.insert(fMapaSensor.begin(), fMapaSensor.end()); // Alternativa: construir un 'map' desde un 'multimap' tipo_map fCopiaTemp(fMapaSensor.begin(), fMapaSensor.end());

Pero esta alternativa es costosa en tiempo (en general, la copia tardar ms que la eliminacin ya que puede requerir llamadas a los constructores de los elementos contenidos) y espacio (se estarn duplicando los elementos en los dos contenedores). En ese caso, puede ser preferible optar por la eliminacin. La funcin del siguiente ejemplo, vlida para un multimap genrico (T), realiza esa labor53:
template <class T, class Iterator> inline Iterator eliminar_duplicados(T &t, Iterator first, Iterator last) { // La funcin devolver un iterador al ltimo elemento del que // se hayan eliminado duplicados o 'end()', si no hay duplicados Iterator iter_ret = t.end(); // Tiene algo el contenedor? if (!t.empty()) { // Sinnimo para las bsquedas con 'equal_range()' typedef typename std::pair< Iterator, Iterator > tipo_rango; // Al inicio del rango pasado Iterator iter_ini = first; // Mientras no se llegue al final del rango pasado... while (iter_ini != last) { // Obtener el rango de elementos de clave // igual a la actual ('iter_ini->first') tipo_rango rango = t.equal_range(iter_ini->first);

53 La funcin tiene un pequeo problema: lo ve? Se trata de que, como last no apunte a un elemento donde comienza un rango (una clave o el end() del contenedor), se eliminarn ms elementos de los que se desean. Dentro del bucle while se da por supuesto que se quieren eliminar todos los elementos de la clave apuntada (en cada vuelta), por iter_ini: la funcin equal_range() devuelve todos los elementos. As que, si el iterador last, pasado a la funcin, apunta al medio (a cualquier elemento donde no comience una nueva clave), no se tendr en cuenta, eliminndose todos los elementos (menos el inicial) de clave iter_ini. La alternativa? Crear un bucle while interno, que recorra y elimine los elementos, partiendo de iter_ini y comprobando que no se llega a last.

Revista Sntesis - Junio de 2006

63

Contenedores asociativos (y IV)

C++

// Se ha encontrado la clave en el contenedor? (esto es superfluo: debera) if (iter_ini->first == rango.first->first) { // Saltando el primero, se deja un elemento de // clave 'rango.first->first' en el contenedor ++rango.first; // Hay algn elemento con la misma clave? if (rango.first != rango.second) { // Almacenar valor de retorno iter_ret = iter_ini; // Eliminar rango '[(iter_ini + 1), rango.second[' t.erase(rango.first, rango.second); // Por seguridad: situarse en el elemento que ha quedado iter_ini = t.find(iter_ini->first); } } // Siguiente elemento ++iter_ini; } } return iter_ret; } // Ejemplo de utilizacin de la funcin para eliminar registros duplicados eliminar_duplicados(fMapaSensor, fMapaSensor.begin(), fMapaSensor.end());

Para un multiset, dado que no posee valor mapeado, hay que cambiar, en el cdigo anterior, la sentencia t.equal_range(iter_ini->first) por t.equal_range(*iter_ini) y la sentencia find(iter_ini->first) por t.find(*iter_ini). Si tuviese que utilizar esa caracterstica, me resultara ms sencillo derivar desde multiset y/o multimap y definir, en la clase derivada, el mtodo de eliminacin de duplicados, que tener dos versiones de la misma funcin, una para multimap y otra para multiset.

Completando el ejemplo
Una vez se tienen las estructuras de datos definidas, si se ha acertado con la eleccin de las mismas y con las funciones de utilidad que facilitan su manejo, presentar los datos de una u otra manera es sencillo ya que bastar con solicitar a esas funciones que proporcionen aquellos datos que se deseen. En el ejemplo que se ha desarrollado en este artculo, los mismos datos pueden ser filtrados, ordenados y resumidos de diversas maneras. Ampliar la forma de presentacin de los datos para, por ejemplo, mostrarlos en forma de grfico, tal y como se muestra en la Figura 7, no reviste ningn misterio ni complicacin.

Revista Sntesis - Junio de 2006

64

Contenedores asociativos (y IV)

C++

Figura 7.- Una vista grfica de los mismos datos

Si se mira el cdigo del fichero "SensoFRM.cpp", se ver que los eventos TablaDrawCell(), que contiene la lgica del dibujado de la tabla (un TDrawGrid), y el del evento BtnVerGraficoClick(), que contiene la lgica del manejo del grfico (un TChart), son muy similares. Si se desease ampliar la lgica para, por ejemplo, imprimir los datos de un determinado filtro o exportarlos a un fichero de disco, el manejo sera prcticamente el mismo que el que se realiza para trasladarlos desde los contenedores al formulario.

Revista Sntesis - Junio de 2006

65

Carlos Enrique Rodriguez magoo@grupoalbor.com

Object Pascal

Introduccin a Report Manager


Un generador de informes y su librera Delphi, pensado para independizar nuestra aplicacin de la impresin y sus complicaciones.
Hacer que una aplicacin tenga listados prolijos y bonitos es bastante rpido y simple. Quick Report desde hace unos cuantos aos nos da una mano simplificando la tarea. Si los listados tienen que ajustarse a un formato especfico (una factura, un cheque, un papel membretado), el problema empieza a tomar otro color. No a la hora del diseo, sino cuando suena el telfono y se escucha -la imprenta cambi los mrgenes de corte y ahora las facturas salen desplazadas. Particularmente, pensar en tocar el cdigo que tan bien funcionaba hace un ao, instalar ese componente especial que tantos problemas resolvi, hacer la modificacin al informe, esperar que todo compile bien y rogar que al querido imprentero no se le ocurra cambiar los mrgenes el mes que viene, hace que busque la forma de simplificar el retoque de informes una vez finalizada la aplicacin.

Las alternativas
Si buscamos una alternativa desde nuestro desarrollo, podramos optar entre otras soluciones por:

Archivos INI o XML con coordenadas

Cargar todos los valores en sofisticadas pantallas (ms cercanas a un transportador espacial que a nuestra aplicacin tan amigable con el usuario), que nos obliguen a tener que solicitar coordenadas, valores de impresoras, parmetros propios de la consulta, etc. Poner los informes en DLLs, lo que nos lleva a tener que complicar el desarrollo de esta parte de la aplicacin.

Reinventar la rueda y escribir nuestro propio formato de informes externos. Combinar todas o algunas de las opciones anteriores y terminar de complicar el desarrollo.

Si nuestra eleccin pasa por optar entre las soluciones comerciales que estn disponibles en el mercado (Report Builder, Fast Report, SMR Report, etc) veremos que la oferta es de lo ms variada (tanto en precios como en prestaciones) pero tambin notaremos que el acceso que ofrecen muchas de estas aplicaciones al informe desde Delphi es mediante controles OCX Si por otro lado, optamos por la alternativa free, tenemos varios paquetes, como el ZReport, Free Report y el Report Manager. Entre las ventajas que hacen que resalte el Report Manager del resto de sus competidores figuran todos los beneficios del diseo visual, los informes en archivos externos a nuestro programa, la total independencia de conexin con los datos por parte de cada informe, componentes que nos facilitan la comunicacin entre el programa y los informes, el constante soporte tcnico de su autor y su condicin de Open Source.

Revista Sntesis - Junio de 2006

67

Introduccin a Report Manager

Object Pascal

El Paquete Report Manager


Lo primero que llama la atencin sobre este producto, es su parecido a las herramientas de creacin de informes de Microsoft, lo cual nos facilita mucho la tarea a la hora de tener que derivar trabajo a terceros. Ms de una vez en la poca en que usaba QuickReport he tenido que escribir el esqueleto del informe para pasarselo a otro programador y que lo conectara a la aplicacin. Al tener una interfase parecida al reporteador de ACCESS o de FOX, nuestros pedidos de realizacin de informes pueden ser realizados por un universo mayor de programadores. El caso de la tercerizacin del trabajo nos lleva a otra ventaja del Report Manager por sobre la gran mayora de los informes: Sus archivos externos y su independencia de la aplicacin. Cada informe creado ser almacenado en un archivo .REP (el cual puede estar autocomprimido) y en ese mismo archivo se van a almacenar los datos de la conexin a la base de datos, los parmetros necesarios para ejecutar la consulta, e incluso informacin especfica sobre la impresora (muy til al momento de manejar controladores fiscales o impresoras con juegos de caracteres extraos). La idea de estos archivos es la de tener toda la informacin necesaria para la realizacin del informe en un mismo archivo externo. Por lo tanto, el programador que disee el informe, solamente necesita la IDE de Report Manager y el acceso a la base de datos para escribir y probar el informe. No tiene ninguna necesidad del cdigo Delphi que va a utilizar ese informe. Esto es una gran ventaja desde varios puntos de vista: Si tenemos que contratar a alguien para que nos de una mano con el desarrollo de los informes, podemos contratar a cualquiera sin tener que preocuparnos por la seguridad de nuestro cdigo fuente, ya que nunca lo va a necesitar para su desarrollo.

Podemos trabajar con distintos tipos de conexin accediendo unicamente al archivo .REP. Por ejemplo, si estamos migrando nuestra aplicacin desde MS-ACCESS a Firebird, podemos probar los informes cambiando solamente el modo de conexin desde la IDE de Report Manager, sin necesidad de tener dos proyectos paralelos solamente por los informes.
Un informe, desde su pantalla de previsualizacin, puede solicitar al usuario los valores para cargar los parmetros que se le pasarn a la consulta que devuelva los datos. Esta caracterstica es muy til sobre todo en un departamento de sistemas de alguna empresa, donde se solicitan informes que no tienen cabida en ninguna aplicacin de las realizadas para la empresa. Creando el .REP y cargando desde la pantalla de previsualizacin (desde el IDE de Report Manager o desde cualquier aplicacin que ejecute un archivo .REP) los parmetros, tenemos un listado reutilizable en el mismo tiempo que toma realizarlo con cualquier otra herramienta. Podemos escribir una pantalla de impresin de informes genrica, que dispare los listados que se encuentran en una carpeta determinada. De esta manera, podemos ir agregandole al usuario informes sin necesidad de modificar el ejecutable. Si tenemos usuarios inquietos, podramos ensearles a trabajar con la IDE del Report Manager, a fin de que puedan generar sus propios listados en base a consultas escritas en el informe, a procedimientos almacenados o vistas existentes en la base de datos, etc.

Como se puede ir viendo, esta herramienta nos abre un abanico de posibilidades mucho ms amplio con respecto a su competidor tradicional como es QuickReport. Pero cuidado, veremos que no todo son ventajas a la hora de trabajar con Report Manager. Para empezar a trabajar con el Report Manager, lo primero que deberemos hacer es ir a su pgina oficial (http://reportman.sourceforge.net) y obtener los archivos que nos interesen, ya que se nos ofrecen tres paquetes para descargar: el diseador visual de informes, un componente ActiveX y las libreras de componentes para Delphi/Builder/Kylix.

Revista Sntesis - Junio de 2006

68

Introduccin a Report Manager

Object Pascal

Figura 1: Presentacin de RM

El Diseador Visual de Informes


El aspecto de esta aplicacin es totalmente independiente del entorno de programacin, como se puede ver en la figura 1. Tenemos la pantalla dividida en cuatro partes: los menes, el rbol de bandas, un inspector de objetos y la zona de trabajo que es representada como la hoja sobre la que se imprimir el informe. Desde los menes tenemos acceso a toda la funcionalidad de la aplicacin. Podemos desde generar un nuevo informe hasta alinear los distintos componentes en una banda. En esta zona tambin estn los componentes a utilizar en las bandas. Los componentes disponibles son:

TrpLabel: Son etiquetas de texto.

TrpExpression: Imprimir los valores devueltos por distintas expresiones. Expresiones pueden ser: campos de una tabla, parmetros pasados desde la aplicacin o funciones propias del informe (el nmero de pgina, la fecha de impresin, etc)

TrpShape: Son figuras simples, como recuadros, elipses, lneas, etc. TrpImage: Permite el ingreso de figuras en formato BMP o JPG. TrpChart: Componente que permite el manejo de grficas de barras, tortas, etc. TrpBarCode: Representa, mediante el tipo de cdigo de barras seleccionado, una expresin.

Revista Sntesis - Junio de 2006

69

Introduccin a Report Manager

Object Pascal

El rbol de bandas representa la jerarqua de las distintas bandas que conforman el informe. Por ejemplo, un rbol comn en un informe simple se ve como Subinforme0 ( se pueden encadenar varios informes para que salgan en una sola hoja), una banda cabecera de pgina (se va a imprimir al comienzo de todas las pginas), la banda detalle (donde, al igual que en Quick Reports, se recorrern los registros de la base de datos) y la banda pie de pgina (al final de todas las pginas, por ejemplo conteniendo el nmero de pgina, la fecha del informe, etc.) El inspector de objetos, al igual que en Delphi, va a permitirnos acceder a los valores de configuracin de cada componente. Una caracterstica muy prctica son las distintas solapas que organizan los valores por grupo de inters. De esta forma, en un TrpExpression veremos las solapas Todo (con todas las opciones encolumnadas), Posicin (todo lo relativo a la ubicacin en la banda del componente), Texto (fuente, color del fondo, alineacin, etc.) y Expresin (los valores a imprimir, las condiciones de impresin, etc.) La zona de trabajo tiene la forma de una hoja de papel, y es donde van a ir apareciendo las distintas bandas que incluyamos en el informe y sobre las que vamos a ir poniendo los componentes.

El componente ActiveX
A veces tenemos que dejar un rato Delphi para volcarnos a otro lenguaje (una aplicacin en Fox, un nuevo desarrollo en C#, soporte a una aplicacin VB). Para estos casos, el autor nos entrega un componente ActiveX para vincular cualquier aplicacin Windows con nuestros informes. El uso es el estandar para un componente OCX. Se registra con regsvr32 Reportman.ocx y se accede mediante su interfaz de mensajes. Consultando la documentacin del Report Manager se puede acceder a un captulo donde se describen todos los mensajes, un ejemplo en Visual Basic y otro en ASP.

La librera de Componentes
No voy a recorrer componente por componente detallando lo que hacen, ya que la mayora de los componentes estan para realizar tareas del diseador visual o del propio informes que solo sern usadas en el caso de querer desarrollar nuestro propio diseador visual de informes. Las veremos a medida que vayamos profundizando con las distintas y muy variadas posibilidades que dispone Report Manager. Lo que s veremos ms en detalle sern los dos componentes que ms utilizaremos a la hora de mostrar nuestros informes: TVCLReport (o TCLXReport, dependiendo de la plataforma) y TrpAlias. El componente TVCLReport es el que administra el informe, conociendo la ruta del archivo .rep a imprimir, si se mostrar una previsualizacin, la ventana de progreso (para los casos en los que la generacin del informe demore), un ttulo, etc. El componente TrpAlias es el que permitir pasar al informe un DataSet ya abierto en la aplicacin. De esta manera, si tenemos una tabla generada en tiempo de ejecucin (una consulta dinmica por ejemplo), podemos pasarla directamente al informe sin tener que reconstruirla en el informe para que se ejecute cuando se abra.

Ni rosas, ni espinas
No todo son bondades a la hora de trabajar con Report Manager, es una herramienta en constante desarrollo, lo que siginifica que hay partes que todava no estn desarrolladas o pulidas. Por ejemplo, hace un tiempo tena una muy bonita aplicacin que manejaba informes de personas. Cada informe se realizaba con texto enriquecido (RTF) gracias a los componentes Trichxx y era almacenado en la base de datos sin problema. Todo funcionaba de maravillas hasta que el usuario pidi ver esos informes (muy bonitos con sus italicas, negritas y colores) en papel.

Revista Sntesis - Junio de 2006

70

Introduccin a Report Manager

Object Pascal

Ah descubr que mi tan empleado Report Manager no maneja otra cosa que no sea texto plano (al cual se le puede cambiar la fuente y sus propiedades, siempre que sea para todo el texto). Si bien puede parecer una nimiedad, el tema del formato enriquecido en los informes es algo que implementan casi todos los generadores de informes desde sus versiones ms tempranas. Una molestia que encontr con el uso de Report Manager es su fuerte dependencia de libreras externas, que si bien es en casos muy puntuales, no deja de ser molesto. El caso ms notorio es el uso de tecnologa MIDAS. Si la mquina donde instalamos nuestra aplicacin no cuenta con la librera midas.dll o si por un problema de red esta librera est registrada en un equipo al que no tenemos acceso, en lugar del informe saldr un mensaje del tipo no puedo encotrar midas.dll. Para esto, es una buena idea al distribuir nuestra aplicacin no olvidarnos de la librera midas.dll y de registrarla en un lugar al que tengamos la certeza que siempre accederemos en tiempo de ejecucin. Quiero insistir al resaltar el manejo de los listados en forma externa a la aplicacin, ya que nos permite modificar los datos listados en el cliente sin que tengamos que tener instalado el Delphi en la zona. Esto nos da la libertad de poder modificar los campos mostrados, el formato del informe, o hacer una aplicacin nueva mientras estamos con el usuario, dejndonos la tranquilidad que nuestro cdigo no fue cambiado ni accedido en ningn momento (en mi caso, es una tranquilidad saber que nada cambi en el ejecutable a la hora de discutir con algunos usuarios la naturaleza de extraos bugs). Como contrapartida, tenemos que recordar distribuir los nuevos informes con las nuevas versiones que hagamos (nuestra aplicacin ahora deja de ser un ejecutable y algunos archivos de configuracin para transformarse en un ejecutable, los archivos de configuracin y todos los archivos de informes que hayamos escrito), ya que Report Manager no brinda una opcin de incrustar los archivos .rep en el ejecutable tal como lo hace Quick Report. A la caracterstica de los archivos externos, se le suma la conexin independiente del informe con respecto a nuestro ejecutable. Esto tiene las ventajas que ya vimos, pero tambin encierra sus problemas. Si nos manejamos recibiendo los DataSet abiertos por el ejecutable, no deberamos tener problemas de acceso a los datos. Si en lugar de eso, como es el caso de los listados generales, la consulta se dispara en el momento de crear el informe y la consulta se ejecuta desde el informe y no en el ejecutable, deberemos verificar que el informe est apuntando a la base de datos correctamente. Este problema es comn si trabajamos, por ejemplo, con dos servidores, uno de desarrollo y otro de produccin. Si una vez que tenemos terminado nuestro listado contra el servidor de desarrollo no cambiamos el string de conexin al servidor de produccin, o veremos los datos del servidor de produccin o recibiremos un error de conexin por ausencia del servidor. En mi caso, este problema lo soluciono al momento de ejecutar el informe, pasandole la cadena de conexin usada por la aplicacin. A partir de la ltima versin, existe un componente para el manejo de la previsualizacin de los componentes en tiempo de ejecucin. Al momento de escribir estas lneas, el autor no ha terminado de escribir la documentacin sobre el componente, por lo que los experimentos que pude hacer han sido todos en base a pruebas, errores y consulta del cdigo fuente. Espero en breve poder escribir con ms detalle sobre esta caracterstica tan necesaria para cualquier aplicacin que est destinada a ser utilizada por un tercero. Algo que extraamente no viene con QuickReport y parece bastante bsico es la posibilidad de imprimir cdigos de barras. En los listados que realic con QR y necesit imprimir un cdigo de barras tuve que optar o por instalar la fuente del cdigo e imprimir ese label con el font especfico del cdigo, o recurrir a componentes externos que me resolvieran el problema. Si bien una vez que optaba por uno de estos mtodos ya tena el tema resuelto, Report Manager nos soluciona el problema al incluir en su paleta de componentes el TrpBarCode. Ahora agregar el cdigo de barras a nuestros listados se limita solamente a ubicar este componente donde queramos que aparezca el cdigo y seleccionar la norma en que se representarn los datos (en este aspecto, Report Manager es muy completo, ya que la batera de normas reconocibles supera a muchos componentes que se encuentran dando vueltas por Internet). Entre los progresos que viene introduciendo en sus sucesivas versiones, Report Manager agreg la capacidad de exportar en distintos formatos. Entre los formatos que figuran estn: PDF, HTML, Excel, Bitmap, metaarchivo (Report Manager provee un visor para este formato). La verdad que esta caracterstica es muy bien acogida por todos nosotros, pero algo a tener en cuenta es que hay partes donde todava falta pulir un poco los filtros de exportacin. Con esto lo que quiero decir es que puede fallar, dando, por ejemplo, errores de acceso a la base (todava no puede ver en qu condiciones pierde la conexin). Pero si mantenemos en mente que esta aplicacin

Revista Sntesis - Junio de 2006

71

Introduccin a Report Manager

Object Pascal

est en constante desarrollo y que el autor en persona responde las inquietudes y problemas, no dudo que en un breve tiempo estos detalles sern solucionados.

Unas palabras sobre el autor


Report Manager es un paquete distribuido como Open Source bajo licencia MPL, permitiendo que pueda ser utilizado y modificado siempre y cuando se publique el nuevo cdigo. Su autor y responsable del proyecto es Toni Martir. En mi caso particular, hace por lo menos ya tres aos que estoy usando Report Manager en aplicaciones de distinta envergadura, y siempre (sin importar si la pregunta es simple o compleja) he tenido una respuesta de Toni en la lista gratuita de soporte de Report Manager. Es sorprendente el nivel de atencin que brinda a los usuarios de su aplicacin, cuesta creer que sea totalmente gratuida y est atendida por una sola persona. Para casos ms precisos, tambin brinda un servicio de soporte pago, en el cual cobra por hora de soporte exclusiva. Como ya dije, llevo mucho tiempo usando esta aplicacin, y recin al cabo de dos aos tuve necesidad de acceder a este tipo de soporte. Estaba superado por un listado con demasiados grupos, variables y exigencias de formato, y por ms vueltas que le daba al asunto no tena forma de hacerlo funcionar correctamente, mis mensajes en la lista de soporte no terminaban de llevarme a un resultado favorable y el tiempo se me extenda demasiado. Ya acorralado por los plazos, le envi un correo electrnico a Toni con los requerimientos del listado y una base de ejemplo para que me dijera si era posible obtener el listado tal cual lo necesitaba. No solamente me respondi afirmativamente, sino que en menos de 24hrs tena el listado terminado corriendo en mi mquina con una explicacin detallada de todos los pasos necesarios para la realizacin del listado. As que no solamente obtuve un informe listo para entregar, sino que aprend muchsimo con la documentacin recibida. Pero ojo, que la cosa no termina aca, ya que como no poda ser de otra manera, el cliente vi el listado y empez a imaginar una serie de agregados no menos rebuscados. Mi eleccin se cay de maduro. Nuevo correo a Toni con los requerimientos y a vuelta de correo (otra vez en menos de 24hrs) tena el listado con las modificaciones hechas. El pago en estos casos lo hice mediante PayPal, resultando toda la operacin rpida y sin complicaciones.

Un ejemplo prctico
Hasta a ahora los que vimos fue un pantallazo general a una aplicacin que tiene demasiadas aristas como para tratar de cubrirlas en un solo artculo. Para aquellos que nunca trabajaron con Report Manager, vamos a hacer una aplicacin y un listado por dems simple, con una cabecera, un pie de pgina y conectarlo desde una aplicacin Delphi. La tarea se divide claramente en dos partes, por un lado el informe propiamente dicho y por otro la aplicacin Delphi. Con esto lo que vamos a hacer es limar los problemas que puedan surgir en una primera aproximacin a esta aplicacin, de manera que aquellos que deseen utilizarla puedan hacerlo de la manera ms rpida posible.

Primera parte: Creando el informe


Abrimos el Diseador Report Manager (al instalarse se crea un acceso directo en el escritorio, en caso de perderlo, el archivo es repmandxp.exe) y creamos un nuevo informe (desde el men, ARCHIVO - NUEVO) y lo grabamos con el nombre PruebaRM.rep (ARCHIVO - GUARDAR). El segundo paso importante es la conexin con la base de datos, para esto, yendo al men Informe Configuracin de Datos se nos abrir una pantalla como muestra la figura 2. Esta pantalla est dividida en dos solapas:

Revista Sntesis - Junio de 2006

72

Introduccin a Report Manager

Object Pascal

Conexiones de Base de Datos: Elegimos qu tipo de conexin o con qu tipo de controlador nos queremos conectar (BDE, DAO, Interbase, Zeos, etc), y establecemos los parmetros de configuracin. Para el ejemplo, yo eleg Microsoft DAO, fu al cono Aade nueva conexin y escrib PRUEBACONEXION como nombre a mi conexin. Un detalle que puede sernos til es que en un mismo informe podemos tener acceso a distintas conexiones. Si por ejemplo, trabajamos con varios servidores esto puede ahorrarnos unos cuantos dolores de cabeza. La cadena de conexin la arm con el asistente que aparece Figura 2: Configuracin de Datos al pulsar Buscar y como Base de Datos use una de las que vienen con Delphi de Ejemplo: Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\Archivos de programa\Archivos Shared\Data\dbdemos.mdb;Persist Security Info=False.

comunes\Borland

La segunda parte es la solapa llamada Conjunto de datos del Informe: Aca lo que hacemos es crear los conjuntos de datos sobre los que se va a mover el informe. Un conjunto de datos es la consulta SQL de donde se van a obtener los datos para el informe (ya veremos cmo con el componente TrpAlias podemos usar un DataSet de nuestra aplicacin). Haciendo click en el botn Nuevo conjunto de datos se nos abrir un dilogo preguntndonos el nombre de nuestro Alias. Para el ejemplo, puse ALIASPRUEBA. Creado el alias, veremos una pantalla en blanco para que escribamos nuestra consulta SQL: SELECT Company, Contact, Addr1, Addr2, City, ZIP FROM customer WHERE City Like :UNACIUDAD +'%' ORDER BY City La sintaxis de los parmetros, vemos que son iguales que en el componente TQuery, dos puntos y el nombre del parmetro. Como consejo, siempre usen el nombre de los parmetros en maysculas, ya que he tenido algunos problemas de consultas que no devolvan resultados por tener el parmetro definido con maysculas y estar en la consulta con minsculas. Ahora falta declarar el parmetro que incluimos en la consulta. Click en el botn Parmetros, botn Aadir un nuevo parmetro, le damos un nombre al parmetro y veremos una pantalla como la que muestra la Figura 3. En esta pantalla definimos el tipo de datos del parmetro, si el valor es nulo, si el parmetro acepta valores nulos y a qu conjunto de datos est relacionado este parmetro (puede ser uno, varios o ninguno). Dej sin comentar dos controles, visible para el usuario y Descripcin ya que pertenecen a otra de las caractersticas de Report Manager. Los informes pueden ser ejecutados desde fuera de nuestra aplicacin. Al hacer esto, los parmetros se ingresarn desde una ventana de dilogo que mostrar la descripcin del parmetro y lo validar de acuerdo a lo que hayamos ingresado al crear el parmetro.

Revista Sntesis - Junio de 2006

73

Introduccin a Report Manager

Object Pascal
Hasta aqu, conectamos el informe a la base de datos. Ahora solamente nos falta seleccionar los datos a mostrar para su posterior impresin o salida por pantalla. Lo primero que tenemos que hacer es relacionar el subinforme con el conjunto de datos. De esta manera, decimos al informe sobre qu datos queremos que desplace la banda Detalle. Sobre el detalle, arrastrando y soltando vamos a ir poniendo las distintas TrpExpression y en el campo Expresin (el entorno est en castellano hasta en los nombres de las propiedades de cada objecto) vamos a seleccionar el campo que queremos que salga. Para que el informe no quede tan crudo podemos agregar algunos TrpLabels etiquetando los valores.

Figura 3: Pantalla Parmetros

Si queremos agregar una cabecera por cada hoja que se imprima, conteniendo el logo de la empresa, el ttulo del listado, la fecha, etc. tenemos que agregar una nueva banda al informe. Para esto, vamos al men Informe Aadir Cabecera de Pgina y veremos que aparece la nueva banda. La completamos con los datos que se tienen que imprimir en cada pgina (por ejemplo un TrpImage para el logo, un TrpLabel para el ttulo y un TrpExpression para la fecha ). De esta misma manera podemos agregar una banda que aparezca como pi de pgina conteniendo el nmero de pgina, la direccin de nuestra empresa, etc, solamente tenemos que ir al men Informe Aadir Pie de Pgina.

Segunda Parte: La aplicacin Delphi


Para la aplicacin, abrimos Delphi, creamos una nueva aplicacin windows y en el formulario principal aadimos un botn, un TEdit y un componente TVCLReport de la paleta Reportman. Como observacin, es bueno tener en cuenta que al contrario de QuickReport que necesita un TForm, el componente TVCLReport puede utilizarse en un TDatamodule sin ningn inconveniente, pero para este ejemplo opt por instalar todo en el formulario principal para evitar complicar el cdigo. Volviendo al ejemplo, la idea es ingresar la ciudad a imprimir en el Edit y con el botn disparar el informe de todas las empresas de esa ciudad. Una consideracin aparte. La instacin de los componentes no es algo intuituvo, ya que son un conjunto considerable de paquetes que deben ser instalados en un orden especfico. Pero esto tambin depende de la versin de Delphi que estemos utilizando y lo que queramos hacer. Por ejemplo: Para Delphi 5, basta con instalar el paquete rppack_del5.dpk para tener los componentes en nuestra paleta. Para Delphi 6 y Delphi 7 La tarea se complica, ya que los paquetes a instalar son varios y debe hacerse en orden: rppack_del.dpk En este paquete estn las clases bsicas, no es visual

Revista Sntesis - Junio de 2006

74

Introduccin a Report Manager

Object Pascal

rppackvcl_del.dpk Son los componentes de la VCL rppackdesisgnvcl_del.dpk En este paquete estn los componentes que nos permiten acceder y disear nuestro propio Editor de informes. rppackdesigntime_del.dpk Son los editores de diseo que vemos en tiempo de diseo. rppackv_del.dpk Esta es la paleta de componentes visuales CLX. Si no se va a trabajar con otra plataforma que no sea Windows, sugiero no instalarlo., rppackdesign_del.dpk Interfase visual en CLX al Editor de informes. Nuevamente, si no se piensa desarrollar un sistema multiplataforma, si hay que instalarla. Es comn ver la mezcla de componentes VCL y CLX en la aplicacin a causa de errores al momento de seleccionar los componentes de la paleta. Para Delphi 8: Cambia un poco la metodologa, aunque tambin cambia el modo de ver las cosas gracias a .NET. En lugar de paquetes tenemos los proyectos, que se deberan instalar: Reportman.bdsproj Contiene los componentes de acceso a Report Manager. ReportmanDesign.bdsproj Aca encontraremos el acceso al Editor de informes Para Delphi 2005: rppack_del2005.bsdproj El paquete base, es no visual rppackvcl_del2005.bsdproj VCL con los componentes bsicos para el acceso a los informes rppackdesignvcl_del2005.bsdproj VCL de la interfaz con el diseador del informes rppackdesigntime_del2005.bsdproj Editores para el tiempo de diseo

Una vez superada la instalacin de los componentes en Delphi, vamos a soltar en nuestro form el componente TVCLReport (si optamos por instalar los componentes CLX en Delphi 6/7, seguramente tendremos TCLXReport, pero para el ejemplo es lo mismo uno u otro).
En el componente TVCLReport cargamos el nombre del archivo .REP que contiene el informe en la propiedad Filename, nos aseguramos que est en TRUE la opcin Preview y en Title ponemos un bonito ttulo a nuestro informe, ya que es la cadena que se mostrar en el caption de la pantalla de vista preliminar. En el evento OnClick del botn, primero cargo el parmetro que toma del Edit (en el ejemplo lo llam edCiudad) y a continuacin ejecuto el informe.
procedure TForm1.Button1Click(Sender: TObject); begin with informe do begin //Pasamos el parmetro Report.Params.ParamByName ('UNACIUDAD').AsString:= TRIM(edCiudad.Text); //Ejecutamos el informe Execute; end; end;

Con esto ya tenemos un informe simple que ejecuta una consulta con un parmetro pasado desde una aplicacin Delphi. Como se puede ver, no hay grandes complicaciones desde el lado de la aplicacin, ya que, en este caso, optamos por delegar en el informe la resposablidad de la consulta, dejando a la aplicacin solamente la tarea de levantar (y validar de ser necesario) el parmetro que enviaremos al repote.

Revista Sntesis - Junio de 2006

75

Introduccin a Report Manager

Object Pascal

Finalizando
Hasta ahora solamente hice una muy superficial presentacin de la aplicacin. Las posibilidades son muchas y de lo ms variadas (desde lo visto en este artculo hasta informes que piden sus propios parmetros al usuario, pasando por un servidor de informes va web), por lo que iremos revisando ms en profundidad cada una de estas variantes a lo largo de sucesivos artculos y de las sucesivas versiones de esta excelente herramienta. Como dato final, recomiendo a todos los usuarios de esta herramienta que se actualicen a la ltima versin, que se inscriban en la lista de correo del Report Manager, y que no dejen de enviarle a Toni Martir las inquietudes y sugerencias que encuentren (y si se animan, podran implementarlas, ya que es Open Source) a fin de ir mejorando el Report Manager en sus futuras versiones.

Revista Sntesis - Junio de 2006

76

Tavo Ibaceta - tavo_ib@grupoalbor.com

Delphi

El depurador de Delphi (I)


Primera introduccin al depurador de Delphi 6. Para qu sirve?
No es ninguna novedad que la palabra bug significa bicho en ingls. Adems de no ser ninguna novedad tampoco tiene mucha importancia, pero siempre queda bien comenzar un artculo con apostillas histricas. All por los aos 40 del siglo pasado (s, del siglo pasado), cuando los circuitos integrados no eran an ni ciencia ficcin, las computadoras utilizaban bulbos, lmparas, de sas que generaban luz y calor, atrayendo a los insectos. Cuenta la leyenda que una adorable seora llamada Grace Murray Hooper anot en su libreta la causa de un fallo de la computadora Mark II: Rel n70 Panel F bug en Rel. Aunque desconocemos si se trataba de polilla o araa, lo cierto es que haba un bug en el programa. La adorable seora pas a la posteridad, pero no por andar matando bichitos sino por ser la precursora del lenguaje COBOL. Adems, es posible que no la vea de manera tan adorable si le cuento que se trataba de una almirante de la Marina Estadounidense y que la Mark I se utilizaba en ese entonces para calcular los ngulos en los que deban apuntarse los nuevos caones de la Marina. Lo cierto es que los bulbos han quedado en la prehistoria siendo hoy reemplazados por lo que conocemos como circuitos integrados. El trmino bug ha perdurado y hoy se sigue empleando para indicar problemas en circuitos internos y en archivos. Claro que la traduccin desbichar no hubiera quedado elegante, as que nos referiremos al Debugger como depurador, el depurador de Delphi 6. Acaso tiene sentido hoy un artculo sobre el depurador de Delphi 6? Buena pregunta. Podra decirse que la poltica de Borland respecto a actualizar a Delphi .NET est jugando de manera decisiva respecto a este tema. Actualmente son muy pocas las empresas que estan desarrollando en Delphi.NET que no sea para migrar sus anteriores desarrollos al nuevo paradigma del seor de gafas. Pero es difcil asegurar que, para un nuevo desarrollo, Delphi.NET sea indudablemente la mejor eleccin. A a ello puede sumarse la realidad del parque de Hardware de algunos pases donde las computadoras de ltima generacin son una muy difcil inversin, por ello a Borland le est costando mucho convencer a sus clientes que programen para .NET ya que siempre es mucho ms difcil vender software que obligue a cambios de hardware. Hay Win32 para rato?.

El depurador integrado
Las personas que deciden casarse y los desarrolladores de software saben que no siempre las cosas salen como uno espera. Haya ledo o no en la entrega anterior de Sntesis el artculo sobre el compilador, le resultar obvio que no es suficiente con que el cdigo sea compilado sin errores. Un cdigo compilado puede contener errores mucho mas difciles de encontrar. La tarea puede resultar ardua y frustrante. El depurador de Delphi puede servirnos para interceptar excepciones, colocar puntos de ruptura, evaluar expresiones e inspeccionar el cdigo fuente compilado, entre otras cosas. Se dice que el depurador est integrado porque est disponible cuando trabajamos con el cdigo fuente dentro del entorno de desarrollo. Por defecto, el depurador queda activado cuando se instala Delphi. Si desea desactivarlo puede desmarcar la casilla de verificacin Integrated debugging en la la pestaa General de la ventana Debugger Options.

Revista Sntesis - Junio de 2006

77

El depurador de Delphi (I)

Delphi

Si lo tiene habilitado, puede verlo en accin cuando una excepcin ocurre o es elevada 54 en un programa ejecutado desde el entorno de desarrollo. Lo primero que ver ser un mensaje avisndole sobre la excepcin (como se ve en la figura 1). Luego el depurador integrado entra en accin permitindole acceder a todas sus prestaciones, que intentar detallar a continuacin.

Tooltip Expression Evaluation


No existe una traduccin nica para Tooltip Expression Evaluation pero podemos decir que hace referencia a esa herramienta que sirve para mostrar en tiempo de Figura 1 - El depurador nos avisa de un error depuracin el valor de de una expresin en el cdigo fuente en el momento en el que la ejecucin se detiene en un punto de ruptura. Para verlo en accin puede colocar el cursor del ratn sobre una expresin con el depurador cargado y la ejecucin detenida. Podr ver en una ventana emergente, similar a una pista (Hint) el valor de la expresin, como se ve en la figura 255. En el caso de que el valor de la expresin no est disponible por las optimizaciones que realiza el compilador, la ventanita dir algo como Inaccesible value. Esta herramienta se puede habilitar/deshabilitar desde la ventana de configuracin del editor (Tools | Editor Options | Code Insight)

Figura 2 - Estoy en EOF?

54 Decir que una excepcin ocurre es hablar de una manera abstracta. Cuando se ejecuta un cdigo con errores no previstos en el cdigo se est -de hecho- ante un caso de excepcin. Las consecuencias pueden ser -como es obvio- imprevisibles. Otra cosa distinta es que nuestro cdigo detecte que se trata de un caso de excepcin previsto, como la falta de ciertas condiciones (falta de memoria, el archivo no existe, etc). Entonces se pueden elevar excepciones, que irn a parar a las manos del seor except, est dicha clusula en nuestro cdigo o en el del ciclo de mensajes de la aplicacin, lo cual es mas aconsejable (no abuse del try except). 55 Es posible que note un poco rara la foto tomada de mi editor de texto. Efectivamente, la configuro -por una cuestin de gustos- con el fondo negro. Recuerde que en algunas ocasiones no es lo aconsejable. Puede interiorizarse de ello en el artculo Salud en informtica (Demetrio Quirs) publicado en este mismo nmero.

Revista Sntesis - Junio de 2006

78

El depurador de Delphi (I)

Delphi

La pila de llamadas
No es nada raro que durante la ejecucin de un programa desde el IDE de Delphi nos encontremos con una excepcin inesperada. Posiblemente una excepcin que nosotros mismos hemos escrito, pero que no esperbamos encontrar en este momento. Qu ha pasado? Y a t quin te ha llamado? Atrs quedaron los das de la programacin funcional que tanto juego haca con D.O.S. y su mecnica unidireccional. Las famosas ventanitas que el seor Gates registr como suyas se apoyan en la programacin orientada a eventos. Y la VCL de Delphi es -podramos decir- puro evento56. Luego del error cometido en las figuras 1 y 2 comprend que la tabla Animals.DB que viene con Delphi no contiene un campo llamado Edad y que a veces es mejor utilizar campos estticos. A continuacin cre todos los objetos TField de dicha tabla en tiempo de diseo y me propuse validar el ingreso de datos. Como s que las los nombres de animales no pueden comenzar con nmeros, escrib rpidamente una funcin que me diga si la cadena pasada como parmetro lo hace.
function TForm1.ComienzaConDigitos(Str: String): Boolean; begin Result := Str[1] in ['0'..'9'] ; end;

Una maravilla de la programacin, vea. Esta funcin es llamada desde el evento OnValidate del campo NAME de la tabla Animals.
procedure TForm1.tbAnimalsNAMEValidate(Sender: TField); begin if ComienzaConDigitos(Sender.AsString) then Raise Exception.Create('Los nombres no pueden comenzar con dgitos') ; end;

Prob mi maravilla desde un TDBGrid asociado a la tabla y funcion. Comprob que funcionaba como yo esperaba, al primer intento de ingresar algo como 5 Ornitorrincos obtuve la excepcin esperada y tom la foto que puede ver en la figura 3. Perfecto!, todo funciona como se espera57. Luego escrib la funcin que ingresa datos a la tabla desde otra Tabla y me propuse utilizarla. All comenzaron los problemas (Figura 4). El fantasma del Access Violation, que recorre mi cdigo fuente, apareci sin avisar. Inmediatamente se carg el depurador y la ejecucin se detuvo en la lnea:
Figura 3 - Una excepcin propia

56 Ni las ventanas son invento del seor de anteojos ni la programacin orientada a eventos aparece con MS Windows. Aqu estamos hablando sobre el API Win32 que es -a diferencia de DOS- basado en mensajes en las dos direcciones, lo cual no significa unvocamente *eventos*. Es el cdigo de la VCL el que trasforma algunos mensajes de Windows en lo que conocemos como eventos. 57 La escritura if y de raise y dentro del manejador del evento responde a la necesidad del ejemplo. Evale si no es mejor eleccion colocarlos en un mismo procedimiento que evale la condicin y que eleve una excepcin, dicho procedimiento (que no necesita ser mtodo) puede ser entonces llamado desde cualquier manejador de OnValidate que necesite validar esas condiciones.

Revista Sntesis - Junio de 2006

79

El depurador de Delphi (I)

Delphi

Result := Str[1] in ['0'..'9'] ;

La causa era obvia, el torpe que escribi ese cdigo no previ que una cadena puede estar vaca, y Str[1] apunta -en ese caso- a... hacia dnde apunta?58. Como sea, lo que se llama una violacin de acceso. Como en general no me doy cuenta de lo obvio, ech mano de la ventana de la pila de llamadas ( Call Stack). Puede hacerla visible desde el men View | Debug Windows | Call Stack. Con la ejecucin detenida pude ver qu fu lo que pas: La funcin que ingresaba datos desde otra tabla intent copiar un registro donde el campo NAME no estaba inicializado, la temible cadena nula que mi funcin no esperaba. La ventana Call Stack le permite inspeccionar la cadena de rutinas y subrutinas que resultan en la ejecucin de la lnea de cdigo donde el depurador se haya detenido. En condiciones normales encontrar en esta ventana slo las rutinas definidas en su aplicacin para las cuales haya definido que la informacin de depuracin (debug symbol information en la pestaa Compiler de la ventana Project Options) est disponible.

Figura 4 - Una excepcin escrita por los seores de Borland (EAccesViolation)

En la figura 5 puede ver que a la funcin ComienzaConDigitos() se le ha pasado una cadena nula. Esta ha sido llamada desde el manejador del evento denominado tbAnimalsNAMEValidate y ste a su vez por el procedimiento CopiarNombresATabla. Y quin ha llamado a CopiarNombresATabla? Pues un maldito botn: al fin tenemos a quin echarle la culpa de todo. Le comentaba que en Windows la programacin es bidireccional. Si ha comenzado a programar con herramientas como Delphi es posible que pase por alto que lo que llamamos la VCL es fruto del trabajo de la gente de Borland, que ha encapsulado las llamadas al API Win32 en clases 59. Por un lado existen llamadas al API para darle rdenes al OS. Por otro lado, el OS enva mensajes que la VCL debe saber escuchar para luego traducirlas en -por ejemplo- eventos, eso significa que la cadena de llamadas es mucho ms larga que lo que se ve en la figura 5, para ver la cadena completa necesita utilizar una versin de la librera VCL compilada con informacin de depuracin (debug information). Vamos a ello. Puede ejecutar la ventana de dilogo Project Options desde el men principal en Project | Options, all habilite la casilla de verificacin Use Debug DCUs en la pestaa Compiler. Ahora puede tener ms informacin acerca del mismo punto en el cdigo fuente, asegrese de haber configurado adems el valor Search Path en la pestaa Directories/Conditionals de la misma ventana. Dicho valor especifica la ubicacin de los

Figura 5 - La pila de llamadas

58 Una cadena puede estar vaca, en cuyo caso el operador subndice ('[]') lanzar la excepcin 'ERangeError', ya que se intentar acceder a una posicin de memoria que est fuera del espacio reservado por la cadena. En el caso que nos ocupa, que la expresin 'Str[1]' lance una excepcin cuando la cadena est en blanco tiene que ver con el diseo del tipo AnsiString de la VCL. 59 Este concepto slo es vlido para la plataforma Win32, las clases de la VCL encapsulan llamadas a funciones. No es as en .NET, que proporciona un acceso homogneo a los objetos del OS.

Revista Sntesis - Junio de 2006

80

El depurador de Delphi (I)

Delphi

archivos de su cdigo fuente. El valor por defecto para el Search Path de cada proyecto es $(DELPHI)\Lib\Debug, que a su vez es tomado del valor que figure en Debug DCU Path en la pestaa General de la ventana Debugger Options. Lo que veamos en la figura 5 era -por decirlo de alguna manera- slo cdigo propio. Su cdigo. Ahora, con ms informacin de depuracin puede ver que la cadena de llamadas es mucho mas larga (figura 6), que el procedimiento BitBtn1Click() (el manejador del evento OnClick del botn de marras) fue disparado por el mtodo Click de una clase llamada TControl (un ancestro de nuestro botn) y que la dicha clase tiene un procedimiento de ventana WndProc() que es el encargado de escuchar los mensajes recibidos. En realidad el primer enterado del asunto ha sido el objeto Application, que dentro de su mtodo Run() llama a HandleMessage(). Puede utilizar el men contextual de la ventana de la pila de llamadas para inspeccionar directamente el cdigo del mtodo seleccionado eligiendo View Source. El IDE se posicionar directamente sobre el cdigo en cuestin, trtese del cdigo de los seores de Borland (la VCL), del suyo propio o de cualquier biblioteca de terceros. Hablando de la famosa VCL, si desde la ventana de la pila de llamadas inspecciona su ejecutable con la informacin de depuracin denominada debug DCUs, o simplemente echa un vistazo a la figura 6, podr interiorizarse de la forma y el orden en que todas las llamadas fueron hechas. Para este ejemplo, al hacer clic en un control de la VCL y debido al propio diseo de la VCL, la pila de llamadas va recorriendo los mtodos de los antecesores del control, de abajo hacia arriba. En algunos casos se tratar de mtodos sobreescritos (overriding) en la jerarqua de las clases: puede ver que -con un slo clic del usuario- hay varias llamadas al mtodo Click pero de diferentes clases60. Esto es lo que llamamos la pila de llamadas. A diferencia de lo que ocurre con los gobiernos, aqu s podemos investigar hasta las ltimas consecuencias hasta saber quin origin todo. Para el caso que nos ocupa, recuerde que el culpable de todo no es el objeto Application ni el Botn, sino el torpe que escribi:
Result := Str[1] in ['0'..'9'] ;

Figura 6 - La pila de llamadas (con mas imformacin)

60 Encontrar algo de informacin a este respecto en Sobreescritura de propiedades, Sntesis n 11 - Octubre 2002

Revista Sntesis - Junio de 2006

81

El depurador de Delphi (I)

Delphi

La ventana Watch List


Si me pide que le traduzca semejante cosa le dira algo como Ventana de inspeccin de expresiones digamos que sirve para inspeccionar -siempre en modo depuracin o ejecucin detenida- una lista de valores, que pueden ser variables o expresiones. Puede ponerla en accin desde el men View | Debug Windows | Watch List. Para utilizarla he reemplazado la funcin ComienzaConDigitos() por un procedimiento denominado VerificaNombre(). Por un lado ahora podremos verla (watch) en ejecucin, por otro, obtenemos un ejemplo menos estpido que el anterior. Adems es el mismo procedimiento el que eleva la excepcin cuando no se verifica la condicin necesaria. Repasando, desde el evento OnValidate se llama a VerificaNombre().
procedure TForm1.tbAnimalsNAMEValidate(Sender: TField); begin VerificaNombre(Sender.AsString) ; end;

Y en VerificaNombre() se encarga de elevar una excepcin si el nombre contiene un dgito:


procedure TForm1.VerificaNombre(Str: String); var I : Integer ; HayDigito : Boolean ; begin HayDigito := False ; if Str <> '' then for I := Length(S) downto 1 do HayDigito := HayDigito or (S[I] in ['0'..'9']); If HayDigito then Raise Exception.Create('Un nombre no puede contener dgitos') ; end;

Luego de asegurarme de tener habilitado el depurador puse el cursor sobre la primera lnea del mtodo y presion F4, que es lo mismo que elegir Run | Run to Cursor en el men, algo as como Ejecutar hasta esta lnea donde he situado el cursor61. Luego ejecut el programa de marras hasta que lleg el momento de grabar el registro en la tablita de los animalitos. All el programa mismo fue tomado del cuello y tirado hacia atrs: el
Figura 7 - Ventana de inspeccin de expresiones (Watch List)

61 Todo sea por evitar hablar de los puntos de ruptura (BreakPoint), que trataremos en la prxima entrega

Revista Sntesis - Junio de 2006

82

El depurador de Delphi (I)

Delphi

depurador quera aparecer en pantalla. Apareci, obviamente, con la ejecucin detenida en la lnea de cdigo elegida. Aqu puede verse la utilidad de la ventana Watch List. Puede ir formando la lista de expresiones que quiere vigilar paso a paso mediante la opcin Add Watch del men contextual de dicha ventana. No hay lmite para la cantidad de expresiones a ingresar, lo cual es una ventaja indudable sobre el precitado Tooltip Expression Evaluation y el an no citado comando Evaluate/Modify. En la figura 7 pueden verse los valores de las expresiones vigiladas. Observar que antes de entrar la ejecucin en el bucle for, la variable I an no tiene valor asignado, esto es obra del optimizador del compilador.

Archivo de registro de eventos (Event Log)


Existen ciertas ocasiones en las que no deseamos recorrer todo el programa paso a paso, imagnese un bucle for de miles de elementos. Avanzar una a una miles de lneas de cdigo para encontrar un problema o estudiar un caso puede ocasionar cada del cabello, vejez prematura o divorcios. Tenga en cuenta que tiene una alternativa que consiste en decirle al depurador que siga al programa sin perderlo de vista y que nos entregue un informe detallado a su vuelta. Por qu lneas de cdigo anduvo? Con quin se encontr? Qu Dll's carg? Cmo fueron modificndose esas variables? Cunto valan esas variables antes de cargar determinada Dll? Para ver ese informe debe seleccionar View|Debug Windows|Event Log en el men. Le aparecer la ventana que contiene los mensajes de control. Estos se dividen en 5 categoras detalladas mas abajo. Utilizando su men contextual puede limpiar el contenido (clear events), guardar a un archivo de texto los mensajes, agregar comentarios a los mensajes62, y configurar las propiedades de la ventana (properties). Al configurar las propiedades de la ventana puede elegir qu tipo de mensajes su agregan al log:
Breakpoint messages: Si se habilita, el depurador agregar un mensaje al registro (log) cada vez que se encuentre un punto de ruptura o una excepcin sea elevada. El mensaje incluye la direccin de memoria del punto del programa que est siendo depurado donde se ha colocado el punto de ruptura, adems de informacin adicional sobre ste, como la unidad donde se encuentra, nmero de lnea, condicin, etc. Process messages: Si se habilita, el depurador agregar un mensaje al registro (log) cada vez que un mdulo (dll, bpl) se cargue o descargue de memoria por el proceso principal. Thread messages: Si se habilita, el depurador agregar un mensaje al registro o log cada vez que un hilo de ejecucin sea creado o destruido mientras dure el proceso principal63. Output messages: Si se habilita, el depurador agregar un mensaje al registro o log cada vez que su programa o uno de sus mdulos haga una llamada a OutputDebugString. Window messages: Si se habilita, el depurador agregar un mensaje al registro o log cada vez que un mensaje de Windows sea emitido o recibido por el programa (o una de sus ventanas 64) que est siendo depurado. La entrada en el registro o log contendr informacin acerca del mensaje, como su nombre y sus parmetros. Tenga en cuenta que estos mensajes no sern escritos al log inmediatamente si su programa se encuentra en ejecucin. Ni bien detenga el proceso en el depurador los mensajes sern escritos al log.

62 Puede ser til si guarda los mensajes a un archivo de texto para su estudio posterior. 63 Si desea profundizar en los hilos de ejecucin. Puede consultar la serie de artculos sobre el tema que Salvador Jover escribi para la revista Sntesis, que comienzan en el n11 - Octubre 2002 64 Recuerde que una ventana puede significar tambin un TTimer.

Revista Sntesis - Junio de 2006

83

El depurador de Delphi (I)

Delphi

A veces los bucles son largos, o estn dentro de otros, y la bsqueda se hace tediosa verdad?. Ha sentido la necesidad de escribir algo de cdigo para agregar datos a un TMemo durante la ejecucin y evaluarlos posteriormente? Le sugiero consultar en la ayuda la funcin del API OutputDebugString(), con ella puede -desde el cdigo fuente- enviar mensajes al depurador y pasarle una cadena como parmetro 65. En dicha cadena puede introducir los valores que desee evaluar luego, desde la ventana Event log. Slo debe asegurarse de agregar Windows.pas en la clusula uses y luego simplemente escribir algo como:
S1 := 'La Variable X1 vale: ' + IntToStr(X1) ; Windows.OutputDebugString(PChar(S1)) ;

Donde S1 es de tipo String y X1 -obviamente- un entero. Ocurre que OutputDebugString() recibe un parmetro de tipo PChar as que deber hacer la conversin desde String para poder utilizarla.

Conclusin
Siempre queda bien colocar la palabra conclusin al final, aunque no haya ninguna posible, como en este caso. No se puede esperar mucho de una introduccin tan bsica. Recuerde que an nos quedan por recorrer los puntos de ruptura, adems de responder la pregunta inicial: Para qu sirve un depurador?. Una respuesta simple sera Para encontrar y eliminar bugs Pero eso nos lleva a otra pregunta: Qu es un bug? Los bichos an estn ah, y hay quien dice que nos van a sobrevivir.

65 La llamada a la funcin 'OutputDebugString()' del API de Windows, enviar la cadena que se pase a la funcin a cualquier aplicacin que se haya registrado en el sistema como receptora de cadenas de depuracin.

Revista Sntesis - Junio de 2006

84

Sebastian Silva - silvasebastian@hotmail.com

Ingeniera

Introduccin a la Gestin de Proyectos


Sabemos programar, pero, sabemos gestionar lo que programamos?

Comencemos
Seguramente a algunos les parecer familiar el nombre "Gestin de Proyectos", a otros tal vez les parezca nuevo o lo hayan conocido por su nombre en ingls "Project Management" y finalmente puede que no lo hayan odo nunca. Para todos los que estn interesados en saber de qu se trata este ttulo que suena tan bien y por qu no?, tambin para aplicarlo, va el presente artculo.

Qu es un proyecto?
Muchos afirmaran que un proyecto es una "tarea", la "codificacin de un programa" o un "relevamiento, anlisis, diseo y desarrollo de una aplicacin". Bien, para comenzar con un poco de teora definimos que:

Un Proyecto es una empresa temporal que se asume con el fin de crear un producto, servicio o resultado nico siendo creado para elaborarse progresivamente.

Ahora, ms en detalle, qu significa que sea Temporal?:


Se dice que un proyecto debe ser temporal para ser considerado como tal porque debe contener en su misma definicin, un momento de comienzo y un momento de finalizacin. El final de un proyecto se alcanza cuando el objetivo del mismo es cumplido o claramente no se cumplir (cancelacin del proyecto).

Seguimos... qu significa que se produzcan Productos nicos, Servicios nicos o Resultados nicos?
Las tareas a ejecutar para conseguir el objetivo del proyecto no deben haberse realizado antes. Este concepto se aplica para un producto (un sistema informtico por ejemplo), un servicio (brindar un nuevo servicio de pos-venta al cliente) o un resultado (aumentar el nivel de ingresos en el prximo trimestre).

Revista Sntesis - Junio de 2006

85

Introduccin a la Gestin de Proyectos

Ingeniera

Por ltimo... cmo definimos que sea elaborado temporalmente?


Para lograr el objetivo del proyecto se dice que el mismo ser elaborado en pasos, fases y procesos definidos de antemano y controlados a medida que avance el proyecto. Cuidado, muchas veces los proyectos pueden ser confundidos con meras operaciones ya que son ejecutadas por personas, utilizan recursos y son planificados y ejectuados. La diferencia debe surgir de la pregunta "Estoy completamente seguro de qu necesitamos ejecutar para conseguir determinado objetivo?", si la respuesta es afirmativa seguramente estaremos hablando de una operacin.

Ejemplos de proyectos y operaciones


S son proyectos: Instalacin de una red de datos en una empresa X Desarrollo de un sistema de facturacin Levantar una casa a medida para el cliente No son proyectos: Un Servicio de Atencin al Cliente La ejecucin de un plan de contingencia Instalacin de un sistema enlatado sin mayores configuraciones Contruccin de una casa estndar, tarea ya realizada en numerosas oportunidades Ahora que tenemos en claro de qu estamos hablando cuando decimos Proyecto, vamos a nuestro tema: Qu es la Gestin de Proyectos? Si bien definimos que un proyecto puede existir en cualquier industria o actividad (como el caso de la construccin de una casa a medida) vamos a analizar conceptualmente la metodologa de gestin de proyectos, focalizando la visin del artculo a proyectos informticos o tecnolgicos. Podemos definir a la Gestin de Proyectos como el conjunto de herramientas, habilidades, conocimientos y mtodos para minimizar la incertidumbre en la ejecucin de un proyecto y poder as maximizar las posibilidades de que el mismo finalice en los tiempos indicados, con el costo previsto y la calidad requerida.

Quin es quin?
Hablando de proyectos, existen las personas interesadas, o sea, personas que de una u otra manera estn interesadas en el proyecto. Los principales interesados son:
Equipo de proyecto: Las personas que ejecutarn el proyecto (arquitectos, desarrolladores, analistas funcionales, etc.)

Revista Sntesis - Junio de 2006

86

Introduccin a la Gestin de Proyectos

Ingeniera

Equipo de direccin de proyecto: Personas que realizarn las tareas de Gestin del Proyecto (gerentes de proyecto, lderes de proyecto, etc.) Gerente de Proyecto: El gerenciador de todo el proyecto, puede ser una sola persona o el rol puede ser compartido. Esponsor del Proyecto: La persona, institucin o entidad que autoriza el proyecto y destina el dinero y recursos necesarios para para su ejecucin

Y qu ms?
Tal vez uno de los trminos ms utilizados en la gestin de proyectos y en algunas metodologas de desarrollo de software sea el entregable. Un entregable es un tem/elemento tangible, medible y cuantificable. En desarrollo de software, un entregable puede ser un componente, un programa ejecutable, un documento de especificaciones, un diagrama UML, etc.

Conociendo qu es? vamos a seguir con cmo es?


Existe una organizacin llamada Project Management Institute (Instituto de Gestin de Proyectos) www.pmi.org que fue la encargada de recolectar, organizar y definir una determinada metodologa para realizar la gestin de proyectos. Dicha entidad public un libro denominado PMBOK por sus siglas en ingls Project Management Body Of Knowledge (Gua en la Gestin de Proyectos), el cual brinda un lineamiento formal y progresivo en el modo de gestionar los proyectos como tambin as en las tcnicas y herramientas a utilizar. Dicho libro, se ha vuelto una especie de biblia para las empresas interesadas en utilizar la metodologa mencionada y para los Gerentes de Proyectos (Project Managers). Tanto se est utilizando esta metodologa (o disciplina) que existe un proceso de certificacin (tal cual lo posee Microsoft, Cisco, Sun, etc.) llamado PMP, tambin por sus siglas en ingls Project Management Professional -Profesional en Gerencia de Proyectos- el cual es solicitado muchas veces por grandes compaas como un requisito para las personas encargadas de dicha responsabilidad.

Qu dice la famosa biblia?


PMBOK se define a s mismo como una gua de Gestin de Proyectos independiente de cualquier empresa y proyecto. Es perfectamente adaptable tanto a una organizacin unipersonal como a una gran corporacin; las diferencias en estos casos sern bsicamente el nivel de formalidad que se utilizar y el nivel de documentacin que se generar. PMBOK plantea la siguiente estructura:

Entorno de Trabajo de Gestin de Proyecto


Bsicamente se plantea el contexto en el que trabajar el Gerente de Proyecto como tambin as el ciclo de vida.

Revista Sntesis - Junio de 2006

87

Introduccin a la Gestin de Proyectos

Ingeniera

Los estndares de Gestin de Proyectos


Captulo en el cual se muestran los procesos de gestin de proyectos a ejecutar en cualquier proyecto.

reas de conocimiento
En este gran captulo se listan y detallan cada una de las reas de conocimiento recomendadas con todas sus entradas, procesos y salidas. Lo que har en esta ocasin ser ennumerar las reas de conocimiento con una breve descripcin de cada una de ellas.
Figura 1

Gestin de Integracin de Proyecto: Procesos y actividades que se desarrollarn e integrarn a lo largo del proyecto. Gestin del Alcance: Procesos y herramientas para gestionar el alcance total del proyecto, cules sern sus principales entregables, qu se incluye y explcitamente qu NO se incluye. Gestin de Tiempo: Todo lo referente a estimaciones de tiempo, esfuerzo, cronograma y secuencia de actividades.

Gestin de Costos: Estimaciones de Presupuestos, costos incurridos, costos planeados y valores ganados..

Gestin de Calidad: Plantea los estndares que se planean conseguir, cmo se lograrn y contempla los controles de calidad (pruebas y controles). Gestin de Recursos Humanos: Cmo se gestionarn las personas que ejecutarn el proyecto (equipo de proyecto).

Gestin de Comunicacin: Qu se comunicar a quin, en qu momento y cmo.

Gestin de Riesgos: Riesgos que pudiesen ocurrir durante el desarrollo del proyecto y las tareas a ejecutar a fines de reducir el impacto de su ocurrencia en el proyecto.

Gestin de Adquisiciones: Todo lo referente a contrataciones (o licitaciones).

Seguimos con los conceptos


Para reducir la incertidumbre que impone cada proyecto y ordenar de una forma legible la totalidad de tareas que proponen un nuevo proyecto, el desarrollo del mismo se divide en diferentes Fases, desde la concepcin hasta la completitud. La totalidad de las fases es lo que se define como Ciclo de Vida del Proyecto y en este punto en particular, dependiendo de la industria, proyecto y empresa se puede utilizar un ciclo de vida de proyecto diferente en cada caso.

Revista Sntesis - Junio de 2006

88

Introduccin a la Gestin de Proyectos

Ingeniera

Figura 2

Caractersticas de las Fases del Proyecto


Cada fase del proyecto es identificado por la completitud de uno o ms entregables. Los entregables, y por consiguiente las fases, siguen una secuencia lgica para asegurar la correcta completitud del proyecto. Generalmente, el fin de una fase es identificado por la revisin de los entregables y la performance del proyecto a la fecha actual.

Caractersticas del Ciclo de Vida del Proyecto


El ciclo de vida del proyecto sirve para definir el comienzo y fin del proyecto. Tambin define las actividades de transicin entre cada fase y cules sern las actividades a completar en cada una de ellas.

Ciclo de vida del Proyecto vs. Project Management


Dentro de cada fase del proyecto, ejecutamos los procesos determinados de Gestin de Proyectos y dependiendo de la fase en la que nos encontramos utilizaremos tcnicas o herramientas acordes. Como se ve en la Figura 1, cada fase genera Entregables de Gestin de Proyectos, los cuales sirven principalmente para informar a los interesados del proyecto, para conocer el estado de avance y para tomar medidas adecuadas en casos de desvos sobre lo planificado. Para cerrar el tema de qu es un Proyecto y que no, podemos ver la Figura 2, la que nos muestra cundo un proyecto se transforma en un producto, lo cual no quiere decir que siempre que se ejecute un proyecto se busque vender un producto como estamos acostumbrados sino simplemente que el objetivo del proyecto entr en un ambiente de produccin. Un producto puede ser un Sistema de Gestin de Clientes para el departamento de ventas de nuestra empresa y no necesariamente lo venderemos al pblico, simplemente se detect la necesidad del sistema y se decidi ejecutar un proyecto para conseguirlo.

Revista Sntesis - Junio de 2006

89

Introduccin a la Gestin de Proyectos

Ingeniera

Figura 3

Ms conceptos...
Dentro de cada fase, las tareas que se ejecutan se dividen en un grupo de cinco procesos:
Grupo de Procesos de Iniciacin: Tareas que autorizan la ejecucin de la presente fase (verificando los entregables de la fase anterior). Grupo de Procesos de Planificacin: Se planifican las tareas que se ejecutarn a los fines de obtener los entregables de la presente fase.

Revista Sntesis - Junio de 2006

90

Introduccin a la Gestin de Proyectos

Ingeniera

Grupo de Procesos de Ejecucin: Se realizan las tareas planificadas integrando personas y recursos.

Grupo de Procesos de Monitoreo y Control: Se mide y monitorea el progreso de las actividades regularmente para detectar de forma temprana desvos a lo planificado y poder tomar acciones correctivas lo ms pronto posible.

Grupo de Procesos de Cierre: Se formaliza el cierre de los entregables que entrega la fase.

Dentro de cada Grupo de Proceso existen tareas determinadas, que por una cuestin de simplicidad no voy a mencionar, pero s pasar directamente a un ejemplo. Para llevar el ejemplo a un caso prctico y reducido suponemos un proyecto muy simple de 4 fases: Inicio, Planificacin, Construccin y Cierre. Ahora se preguntarn: qu hacemos?, a lo que contestar: primero miremos nuestras fases y grupos de procesos en la Figura 3 para comprender lo explicado anteriormente. Como vemos el la figura, cada fase ejecuta los cinco grupos de procesos, dependiendo de cul sea la fase en la que se encuentra en ese momento sern las tareas a realizar de cada uno de ellos, por ejemplo: En el grupo de Procesos de Inicio, hay tareas que sirven para validar el correcto cierre de la etapa anterior, lo que determina si el proyecto est en condiciones de pasar a la siguiente fase. Como seguramente habrn notado, esta tarea no puede ejecutarse en la primera fase. De la misma forma, hay muchas traeas que dependen de la fase en la que se encuentra. Si bien PMBOK sugiere las tareas a realizar en cada grupo de proceso, tambin deja claro que el Gerente de Proyecto es el que determina sto, o sea, define la cantidad de fases, qu tareas se ejecutarn en cada fase y cmo se llevar adelante al proyecto. En la misma figura, podemos observar que cada fase se encuentra superpuesta de alguna manera a la anterior, sto es con un propsito, ya que el Gerente de Proyecto puede determinar que una fase comience antes de completar la anterior, hago esta salvedad para mostrar la flexibilidad que se plentea de acuerdo al proyecto que estemos manejando.

Ciclo de desarrollo.. suena conocido...


Muchos abrn odo hablar de ciclos de desarrollo de software como Proceso Unificado (UP por sus siglas en ingls o RUP: Rational Unified Process), ciclo de vida espiral, ciclo de vida escalonado y muchos ms. Seguramente, en este momento el lector se estar preguntando: es esto algo totalmente nuevo? qu relacin tiene con el ciclo de vida que ya utilizo o ya conozco?. La respuesta tiene su lado simple y su lado complejo; comenzaremos por la simple y tomaremos como ejemplo el proceso unificado ya que se est utilizando mundialmente como un estndar en el desarrollo de software.

Comencemos por lo simple


La disciplina que se presenta en el presente artculo puede utilizarse con cualquier ciclo de vida de proyecto que se necesite, como se vio en la Figura 1, el ciclo de vida del proyecto se divide en "Salidas de la Gestin del Proyecto" y "Entregables del Producto". En estos dos puntos es donde se realiza la combinacin de la disciplina de Gestin de Proyecto y el ciclo de vida utilizado. Si utilizamos UP por ejemplo, los entregables del producto sern los entregables iterativos de cada fase y las Salidas de Gestin de Proyecto sern los entregables de los procesos de la disciplina planteada. Para que quede an ms claro, el ciclo de vida presentado en la Figura 3 puede ser tratado como un ciclo de vida de Proceso Unificado, cada fase podra ser una iteracin y tener sus propios entregables.

Revista Sntesis - Junio de 2006

91

Introduccin a la Gestin de Proyectos

Ingeniera

Una de las disciplinas que presenta el Proceso Unificado es la disciplina de Gestin de Proyectos, es en ese punto donde utilizaramos "nuestra" disciplina, o sea, la presentada conceptualmente en este artculo.

Sigamos por lo complejo


El lector seguramente se preguntar: cmo realizo la supuesta combinacin entre el ciclo de vida y la disciplina de gestin de proyecto? y ahora es cuando este tema se torna complejo y para utilizar un ejemplo se plantean dos casos extremos:

Primer caso: Realizar la instalacin de un sistema de atencin al cliente. Tiempo estimado: 1 da. Costo estimado: U$S 100

Segundo caso: Desarrollar e implementar un nuevo sistema de telecomunicaciones para cursar todas las llamadas telefnicas y de video dentro de las 14 sucursales de la empresa distribuidas en 7 pases.

Tiempo estimado: 2 aos Costo estimado: U$S 500.000

Les parece que el ciclo de desarrollo de software a utilizar en el primer caso ser similar al segundo? Les parece que los interesados en el proyecto y los directivos de la empresa requerirn la misma informacin para uno que para el otro proyecto? Creen que un desvo negativo del 10% de costo en el primer proyecto tiene el mismo impacto negativo que el mismo desvo en el segundo? Por todas esas preguntas es que cada proyecto debe ser planificado de forma diferente, no solamente en cuanto a su ciclo de vida sino tambin en qu nivel de la disciplina de Gestin de Proyectos se utilizar en cada caso. Justamente, sta es la complejidad: Disear un ciclo de vida y definir qu procesos y herramientas de Gestin de Proyecto utilizaremos en todo el proyecto de acuerdo a la complejidad, control e informacin que necesitemos

Mucha teora... y en la prctica?


Deseando que este artculo haya aportado al menos algunas ideas de qu es la "Gestin de Proyectos" es que describir algunos de los procesos declarados en el PMBOK que considero muy importantes y prcticos pero que generalmente no son utilizados. Cada concepto que se plantea tiene sus procesos ya definidos pero no me explayar en esos procesos ya que escapara al objetivo del presente artculo. Sern mencionados simplemente a modo introductorio para futuras explicaciones.

Revista Sntesis - Junio de 2006

92

Introduccin a la Gestin de Proyectos

Ingeniera

Riesgos
Cuntos de nosotros tenemos la "sensacin" o la idea de que alguna tarea de nuestro proyecto puede traernos problemas? Si tenemos un proyecto que consta en instalar un software en una PC sabemos que es una tarea fcil; ahora, qu pasa si a este proyecto le agregamos dos actividades, una que sea "determinar qu plataforma y sistema operativo est soportado por el software que instalaremos" y una tarea posterior a sta que sea "verificar que la plataforma de la PC y el sistema operativo sean compatibles con el software que instalaremos; si no es as, conseguir otra PC o instalar otro sistema operativo compatibles". De esta forma, lo que estamos haciendo es "mitigando" el riesgo de que nos encontremos con algn inconveniente que retrace la ejecucin de nuestro proyecto; y lo mejor de todo: estamos planificando estas tareas de antemano para adelantarnos a los hechos.. Casos como ste se encuentran diariamente en todos los proyectos, por eso es positivo simplemente detectar todos los riesgos que podamos y planificar cmo responderemos ante esos casos.

Comunicacin
Tanto el espnsor del proyecto (la o las personas que autorizan su ejecucin: finalmente, los que destinan el dinero a tal fin) como todos los interesados necesitarn saber de antemano cmo se gestionar el proyecto, cules sern los recursos que se utilizarn (en personal y financieros) y en qu momento se necesitarn. Tambin precisarn saber si ocurre algn desvo sobre lo planificado ya sea en tiempo o dinero y requerirn conocer las acciones correctivas que se realizarn para recuperar el desvo. Para todo sto es que la comunicacin del proyecto se planifica y gestiona.

Estimacin
Tal vez es la prctica ms utilizada (y odiada) por los desarrolladores. La tpica pregunta "cunto tiempo estimas que tomars para desarrollar esta pieza de software?" o, en caso de compras por ejemplo "cunto dinero estimas que nos costar la adquisicin de esta mquina?". Bien, para contestar esta pregunta, existen muchas tcnicas y mtodos ya sea para estimar duraciones o costos de actividades y proyectos. Un mtodo de estimacin muy conocido y utilizado se denomina PERT (por sus siglas en ingls Program Evaluation and Review Technique, lo que significa Tcnica de Revisin y Evaluacin de Programas), el cual recibe tres parmetros, la duracin estimada (duracin ms probable), la duracin estimada de forma pesimista y la duracin estimada de forma optimista, el resultado es una duracin probable con un porcentaje probabilstico de ocurrencia. La frmula de PERT es:
Duracin Optimista + (4 x Duracin Ms Probable) + Duracin Pesimista --------------------------------------------------------------------6

La funcin mencionada nos brinda una mayor seguridad en la estimacin que realizamos y ahondaremos en detalles probabilsticos en siguientes entregas. Existen muchos mtodos de estimacin y muchos dependen de la industria, existen algunos mtodos como COCOMO II que se aplican al desarrollo de software, pero ste no es un artculo especfico de estimacin as que lo dejaremos para otra ocasin.

Revista Sntesis - Junio de 2006

93

Introduccin a la Gestin de Proyectos

Ingeniera

En muchos casos, lo ms complicado para estimar es el costo de un proyecto, ya sea calculando los costos directos (los costos incurridos para el beneficio del proyecto como salarios del personal con dedicacin completa por ejemplo) como los indirectos (costos no relacionados al proyecto, como por ejemplo gastos de infraestructura, luz, agua, etc.) y trataremos stos temas y muchos ms en siguientes entregas.

Algunos comentarios
Me gustara hacer alguna salvedad respecto a qu se ataca con la presente metodologa o disciplina ya que en el mercado existen algunas bastante mencionadas y utilizadas ultimamente (como RUP por ejemplo, que ya hemos tratado). Por si no qued claro en la lectura del presente artculo, el punto principal que se pretende maximizar con todo esto es simplemente el xito del proyecto; lo que significa que el resultado final debe estar disponible en el tiempo indicado, con los costos previstos y con la calidad adecuada. La diferencia con algunas metodologas famosas es que en muchos casos se ataca un punto particular de un proyecto, RUP por ejemplo ataca las iteraciones que se producen en el desarrollo de software, mientras que CMM o CMMI atacan principalmente la calidad en todos los procesos del proyecto y las Metodologas giles atacan la agilidad, valga la redundancia, en el desarrollo de software reduciendo la burocracia innecesaria que muchas veces existe. Con todo esto intento decir que en muchos casos, dependiendo de la organizacin, hay posibilidades de hacer una combinacin en el cual se utiliza, para la Gestin de Proyectos la disciplina segn el PMBOK, para la ejecucin (si es un proyecto de desarrollo de software) la metodologa propuesta por RUP y como mejores prcticas algunas de CMMI obteniendo de esta forma una disciplina adaptada para los proyectos de la organizacin.

Conclusin, hay alguna?


S que se mencionaron muchos conceptos aislados y seguramente los ejemplos no terminaron de mostrar todo lo necesario para poder comenzar a utilizar esta disciplina, pero el objetivo del presente trabajo es el de servir como introduccin al tema, poder de esta forma despertar curiosidad sobre el asunto y, al menos, saber que existe y conocer de qu se trata.. En posteriores entregas se tratarn temas focalizados en los problemas ms comunes que existen en el desarrollo de software como lo son: estimaciones, controles de calidad y estndares; para luego desarrollar un proyecto paso a paso desde la idea hasta su 'producto'.

Revista Sntesis - Junio de 2006

94

Licencia de las Publicaciones del Grupo Albor


Versin 1

http://www.grupoalbor.com Esta obra se proporciona bajo los trminos de la Licencia de las Publicaciones del Grupo Albor (en lo que sigue, Licencia). Queda prohibido cualquier uso de la obra diferente a lo autorizado bajo esta Licencia o de lo dispuesto en las leyes de propiedad intelectual.

1.Definiciones
a. Obra: es el producto ofrecido bajo los trminos de esta Licencia. b. Autor: es la persona, grupo de personas o entidad que ha contribuido, de cualquier forma, a la produccin de la obra. c. Licenciador: es el Grupo Albor, que ofrece la obra bajo los trminos de esta Licencia. d. Usted: es la persona o entidad que acepta los derechos y obligaciones de esta Licencia. e. Reproduccin: es la creacin de copias de la obra, bien sea en su totalidad o en parte. f. Distribucin: es la puesta a disposicin del pblico de la obra, bien sea en su totalidad o en parte. g. Transformacin: es la alteracin de cualquier parte de la obra. h. Explotacin: comprende la reproduccin, distribucin y transformacin de la obra.

2.Lmites y uso legtimo de los derechos


Mediante el ejercicio de cualquier derecho sobre la obra, usted acepta y consiente las limitaciones y obligaciones de esta Licencia. El licenciador le cede los derechos contenidos en esta Licencia, siempre que usted acepte los presentes trminos y condiciones.

3.Concesin de licencia
Conforme a los trminos y a las condiciones de esta Licencia, el licenciador concede, con mbito mundial y tiempo indefinido los siguientes derechos: Revista Sntesis - Junio de 2006 95

a. Derecho de copia de la obra, total o parcial en mbitos privados. b. Derecho de cita de la obra, en todo o en parte, siempre que la cita se restrinja a mbitos privados y que sea acompaada del Localizador Uniforme de Recursos (URL) del licenciador, nica forma de acceder pblicamente a la obra. c. Derecho de uso, cita, copia y alteracin de cualquiera de los ficheros de cdigo fuente que acompaen a la obra, si los hubiere, siempre que esta accin se realice en las siguientes condiciones: 1. En un medio privado. 2. Los ficheros no estn sujetos a otra licencia. 3. No se indica lo contrario en el momento de su distribucin. 4. No se indica lo contrario en el contenido del propio fichero o en ficheros asociados. 5. No se obtenga ningn beneficio mercantil o remuneracin monetaria. Cuando se ejerza este derecho deber incluirse, si lo hay, el nombre del autor y el Localizador Uniforme de Recursos (URL) del licenciador, nica forma de acceder pblicamente a la obra. Los anteriores derechos se pueden ejercitar en todos los medios y formatos conocidos o por conocer, siempre que el derecho no produzca ninguna alteracin ni transformacin de la obra, la cual deber tratarse siempre como un todo. Los derechos no cedidos expresamente por el licenciador quedan reservados, incluyendo, a ttulo enunciativo pero no limitativo, los derechos establecidos en la seccin 4.

4.Restricciones
La cesin de derechos que supone esta Licencia se encuentra sujeta y limitada a las restricciones siguientes: a. Usted puede copiar o comunicar la obra solamente bajo los trminos de esta Licencia. b. Usted no puede distribuir pblicamente la obra. c. Usted no puede ofrecer o imponer ningn trmino sobre la obra que altere o restrinja los trminos de esta Licencia o el ejercicio de sus derechos por parte de los que adquieran la obra. d. Usted no puede sublicenciar la obra. e. Usted debe mantener intactos todos los avisos que se refieran a esta Licencia y a la ausencia de garantas. f. Usted no puede ejercitar ninguno de los derechos cedidos en la seccin 3 de manera que pretenda o se encuentre dirigida hacia la obtencin de un beneficio mercantil o la remuneracin monetaria.

5.Exoneracin de responsabilidad
El licenciador ofrece la obra tal cual y no confiere ninguna garanta de cualquier tipo respecto de la obra o de la presencia o ausencia de errores que puedan o no ser descubiertos.

6.Obligaciones del Licenciador


Revista Sntesis - Junio de 2006 96

El licenciador se compromete a dar acceso a la obra, de manera pblica y totalmente gratuita, sin mas restricciones que la mera suscripcin.

7.Limitacin de responsabilidad
En ningn caso el licenciador ser responsable ante usted por cualesquiera daos resultantes, generales o especiales, fortuitos o causales, directos o indirectos, producidos en conexin con esta Licencia y/o el uso de la obra, incluso si el licenciador hubiera sido informado de la posibilidad de tales daos.

8.Finalizacin de la licencia
a. Esta Licencia y la cesin de los derechos que contiene terminarn automticamente en caso de cualquier incumplimiento de los trminos de la misma. b. Conforme a las condiciones y trminos anteriores, la cesin de derechos de esta Licencia permanece mientras la obra se encuentre disponible al acceso pblico por cualquiera de los medios proporcionados por el licenciador, dndose esta Licencia por concluida y pasando la obra a dominio pblico en caso de inaccesibilidad de sta por un perodo superior a seis meses.

Revista Sntesis - Junio de 2006

97

Vous aimerez peut-être aussi