You are currently browsing the monthly archive for abril 2010.

Saber idiomas es esencial para poder moverse en el mundo laboral. Aquí viene un pequeño listado con “frases celebres” y su sentido.

Expresiones en el mundo laboral
Expresión Significado
Lo importante es que funcione.
  1. No te quiero ver haciendo cosas que no se ven inmediatamente en la pantalla.
  2. Lo importante es que el cliente pague aunque sea por engaño.
Debemos hacer calidad. Lo importante es que funcione.
Programación orientado a objetos. No sé lo que es programación orientado a objetos, pero debemos hacer calidad.
Lo más importante es la estabilidad. Pon la nueva funcionalidad pero sin errores. Mañana hay entrega.
Cuando el cliente nos lo pide, hacemos la documentación. No pierdas tiempo apuntándote cosas. Un buen empleado lo tiene en la cabeza.
Cuando tenemos tiempo… Nunca.
A estas alturas del proyecto Dos semanas después del comienzo
Tenemos una fecha de entrega
  1. Nosotros quiere decir: yo pongo la fecha y tú la entrega.
  2. Es posible que el cliente no comprará el ordenador necesario hasta dos días antes.
¿Qué podemos hacer para dar un empujón al proyecto? ¿Podrías hacer horas extras?
Esto es un trabajo en equipo. Nuestro código es como un piso de estudiantes: la limpieza impone el más guarro.

La STL (Standard Template Library) de C++ ofrece dos contenedores muy similares: el famoso std::vector y la poco usada std::deque (Double Ended Queue = cola doblemente terminada). Ambos contenedores ofrecen acceso aleatorio a un bloque de memoria continuo. Es decir, son arrays.

¿Qué es la diferencia? El concepto de un vector es un array con un inicio fijo y un fin variable. Una deque es un array con tanto el inicio como el fin variable. Por eso, el contenedor deque es útil cuando quiero tener un acceso aleatorio como en un vector, pero poder insertar delante el índice cero o eliminar elementos al inicio.

Tanto el vector como la deque son contenedores de acceso aleatorio. Sin embargo, internamente tienen una diferencia importante: un vector garantiza que los elementos están guardados en una memoria continua. Es decir, puedo usar un puntero como iterador. De hecho muchas implementaciones usan punteros como iteradores sobre vectores. La deque a contrario no garantiza guardar los elementos en una memoria contigua. Se puede implementar como un vector con un puntero variable al primer elemento del array. Pero el estándar no lo impone. Se puede implementar también, por ejemplo, como dos vectores – uno creciendo hacia indices más altos, el otro creciendo hacia “abajo” para insertar elementos al inicio del contenedor.

Otra diferencia está en los elementos que se reservan de más. Ambos contenedores tienen el método size() que devuelve el número de elementos válidos en el contenedor, pero sólo vector tiene el método capacity(), que devuelve el número de elementos reservados en la memoria.

Por ejemplo, si el método size() de un vector devuelve cinco, entonces mi array aparenta tener el tamaño cinco. Si el método capacity() de la misma instancia de vector devuelve ocho, entonces habrá memoria reservada para ocho elementos. Si añado un sexto elemento al final del vector, la operación es muy rápida, ya que no hace falta reservar más memoria para el un nuevo objeto: el elemento ya existe físicamente y sólo hace falta construirlo con los datos para el nuevo elemento.

Esta reserva de memoria ayuda a mejorar la velocidad cuando añado elementos a un contenedor. Si no fuera así, el contenedor debería reservar memoria para n+1 elementos, copiar los n que ya tiene y escribir el último al final cada vez cuando añado un elemento nuevo. Por eso los contenedores de la STL pueden reservar memoria para más elementos que corresponde a su tamaño.

Se puede forzar la capacidad mínima de un vector con el método reserve(). El contenedor deque no tiene esta característica. (La deque debería tener un método reserve_front() y reserve_back()para esto, pero esto la STL no lo ofrece.)

Por lo tanto, si quiero añadir a un array ya existente un número de elementos conocidos a priori con el método push_back(), es ventajoso usar un vector. No obstante, si ya conozco el número de elementos a la hora de creación del contenedor no tengo esta limitación. Tanto el vector como la deque tienen un constructor que especifica el número de elementos iniciales. Y lógicamente puedo llamar al método resize() en cada momento para especificar el número de elementos en ambos contenedores.

Compara

std::vector vec;
vec.reserve(numero_de_elementos);
for (int i = 0; i < numero_de_elementos; ++i)
{
    vec.push_back(nuevo_elemento);
}

con

std::vector vec2;
vec2.resize(numero_de_elementos);
for (int i = 0; i < numero_de_elementos; ++i)
{
    vec2[i] = nuevo_elemento;
}

La primera versión con reserve() es más flexible cuando quiero sustituir el vector por una lista. Aún así la mayoría de los programadores usaría la segunda forma con resize() que funciona tanto para la deque como el vector.

La limitación de un vector se manifiesta cuando quiero eliminar i elementos del principio. Como su inicio es fijo y el elemento con el índice i será el nuevo con el índice 0, debo copiar todos los elementos de i a n para que ocupen el rango de índices 0 a n-i. El contenedor deque, en cambio, no necesita borrar los elementos. Simplemente sube un puntero al primer elemento en i posiciones y invalida así los elementos a borrar. Estos elementos se quedan en la memoria para ser reciclados si se añadieran nuevas elementos al principio.

La deque requiere un poco más de gestión por tener el inicio variable, pero teniendo en cuenta que la mayor parte del trabajo de un contenedor es trabajar con los elementos y no con el contenedor mismo, la diferencia es mínima. Por lo tanto, conviene optar por una deque siempre y cuando no esté asegurado que no se quieren eliminar elementos del inicio del array.

Un vector es la elección si quiero reservar un número de elementos conocido después de la creación del contenedor, ya que puedo aprovecharme del método reserve() que la deque no tiene. Sin embargo, la reserva previa de elementos se puede sustituir muchas veces por una construcción con resize().

Pero tampoco hay muchas ocasiones en que necesito añadir elementos al principio de un array. Por lo tanto no extraña que la deque es menos conocida que el vector: simplemente se necesita poco. Pero conviene saber de su existencia.

Qué es

Lo que javadoc es para Java, phpDocumentor es para PHP: Una herramienta para generar documentación a partir del código fuente. (Ver artículo sobre
Documentación automatizada.)

Por ser dos proyectos separados, phpDocumentor no es automáticamente compatible con una nueva versión de PHP. Por ejemplo, yo tuve problemas con phpDocumentor 1.4.3 cuando instalé PHP 5.3 y volví a la versión 1.4.2 que funcionaba aunque con muchas advertencias.

Una alternativa a phpDocumentor es Doxygen que puede también documentar código PHP. Una diferencia es que doxygen es un programa, mientras phpDocumentor es una colección de código en PHP. Es decir, genera la documentación con el mismo PHP que usas para ejecutar tu propio código PHP. Por eso necesitas tener también PHP en tu máquina para poder usar phpDocumentor. Sin embargo, no necesitas instalar un servidor web.

Como instalar

phpDocumentor no es un programa sino un código fuente y, por eso, no tiene instalador. Es un archivo que descomprimes en un directorio. Para usar phpDocumentor debes abrir el fichero phpdoc.bat (en Windows) y especificar la ruta a php.exe en la variable phpCli. Por ejemplo

SET phpCli=C:\Program Files\PHP\php.exe

Como usar

Para un proyecto en concreto conviene crearse un script en que especificas todas las opciones de phpdoc.

El siguiente script es un ejemplo. Lo he utilizado para generar la documentación del mismo phpDocumentor. Antes de usarlo hay que ajustar los directorios de entrada y salida y de la instalación de PHP.

echo Generate automatic documenation for phpDocumentor
echo Version from 12.6.2009

rem Directory list must be without spaces between the coma
set phpdoc_dir="C:\opt\PhpDocumentor"
set source_dir="C:\opt\PhpDocumentor"
set target_dir="C:\doc\phpDocumentor"
set title="phpDocumentor Documentation"
set projects=phpDocumentor
rem set output="HTML:frames:default"
rem set output="HTML:frames:earthli"
rem set output="HTML:frames:l0l33t"
 set output="HTML:frames:phpdoc.de"
rem set output="HTML:frames:phpedit"
rem set output="HTML:frames:phphtmllib"
rem set output="HTML:frames:DOM/default"
rem set output="HTML:frames:DOM/earthli"
rem set output="HTML:frames:DOM/l0l33t"
rem set output="HTML:frames:DOM/phpdoc.de"
rem set output="HTML:frames:DOM/phphtmllib"
rem set output="HTML:frames:DOM/phphtmllib"
rem set output="HTML:Smarty:default"
rem set output="HTML:Smarty:HandS"
rem set output="HTML:Smarty:PHP"
rem set output="CHM:default"
rem set output="PDF:default"

cd %phpdoc_dir%
call phpdoc -d %source_dir% -t %target_dir% -ue on -ti %title% -pp off -po %projects% -o %output% -s off
rem pause
exit

rem These are comments, but they will not be executed because the scripts exists before
rem use phpdoc -h to see this

  -f    --filename                name of file(s) to parse ',' file1,file2.
                                  Can contain complete path and * ? wildcards

  -d    --directory               name of a directory(s) to parse
                                  directory1,directory2

  -ed    --examplesdir            full path of the directory to look for
                                  example files from @example tags

  -tb    --templatebase           base location of all templates for this
                                  parse.

  -t    --target                  path where to save the generated files

  -i    --ignore                  file(s) that will be ignored, multiple
                                  separated by ','.  Wildcards * and ? are ok

  -is    --ignoresymlinks         ignore symlinks to other files or
                                  directories, default is off

  -it    --ignore-tags            tags to ignore for this parse.  @package,
                                  @subpackage, @access and @ignore may not be
                                  ignored.

  -dh    --hidden                 set equal to on (-dh on) to descend into
                                  hidden directories (directories starting with
                                  '.'), default is off

  -q    --quiet                   do not display parsing/conversion messages.
                                  Useful for cron jobs on/off default off

  -ue    --undocumentedelements   Control whether or not warnings will be shown
                                  for undocumented elements. Useful for
                                  identifying classes and methods that haven't
                                  yet been documented on/off default off

  -ti    --title                  title of generated documentation, default is
                                  'Generated Documentation'

  -h    --help                    show this help message

  -c    --useconfig               Use a Config file in the users/ subdirectory
                                  for all command-line options

  -pp    --parseprivate           parse @internal and elements marked private
                                  with @access.  Use on/off, default off

  -po    --packageoutput          output documentation only for selected
                                  packages.  Use a comma-delimited list

  -dn    --defaultpackagename     name to use for the default package.  If not
                                  specified, uses 'default'

  -dc    --defaultcategoryname    name to use for the default category.  If not
                                  specified, uses 'default'

  -o    --output                  output information to use separated by ','.
                                  Format: output:converter:templatedir like
                                  "HTML:frames:phpedit"

  -cp    --converterparams        dynamic parameters for a converter, separate
                                  values with commas

  -ct    --customtags             custom tags, will be recognized and put in
                                  tags[] instead of unknowntags[]

  -s    --sourcecode              generate highlighted sourcecode for every
                                  parsed file (PHP 4.3.0+ only) on/off default
                                  off

  -j    --javadocdesc             JavaDoc-compliant description parsing.  Use
                                  on/off, default off (more flexibility)

  -p    --pear                    Parse a PEAR-style repository (package is
                                  directory, _members are @access private)
                                  on/off default off

  -ric    --readmeinstallchangelogSpecify custom filenames to parse like
                                  README, INSTALL or CHANGELOG files

You can have multiple directories and multiple files, as well as a combination
of both options

En la carpeta HTML nuevamente generado debes abrir el fichero index.html para ver la página principal de la documentación. La más práctico es crearse un enlace fuera del directorio con un nombre significativo como “Documentación de bla bla”.

Puedes usar letras con acentos y otras letras no ASCII si guardas el fichero fuente (con el código) con la codificación UTF-8 (con BOM). En general no es mala idea guardar tu código como UTF-8 en lugar del habitual ISO-8859 si quieres usar otros idiomas que el inglés en los comentarios, porque phpDocumentor (y también doxygen) codifican su salida HTML en UTF-8.

phpDocumentor tiene diferentes convertidores. Cada uno compila la documentación en un formato y tema diferente (HTML, PDF, CHM). La página web de phpDocumentor muestra ejemplos de los diferentes temas visuales que permite la documentación en HTML.

Crear la documentación de phpDocumentor y tutoriales

Como ya he mencionado más arriba, phpDocumentor puede crear su propia documentación. Los ficheros fuente de esta documentación están en una subcarpeta “tutorials”. Sin embargo, debes especificar el directorio raíz de phpDocumentor como directorio de entrada a phpdoc, para que se compilen estos tutoriales. (phpDocumentor no procesa documentación en “tutorials” que no esté vinculado a algún código fuente. No puedes compilar sólo tutoriales.) Como proyecto debes especificar “phpDocumentor”. (Ver ejemplo abajo.)

Esta subcarpeta “tutorials” es también un ejemplo de como escribir documentación que no está directamente vinculada a algún objeto dentro del código. Estos tutoriales están escrito en un formato reducido de DocBook. Los elementos DocBook que se compilan están listado en la sección [ppage] del fichero options.ini que viene con cada convertidor. Por ejemplo, mi tema favorito “HTML:frames:earthli” está configurado por el fichero Converters/HTML/frames/templates/earthli/options.ini. Cada convertidor/tema tiene su fichero de configuración options.ini en una carpeta diferente.

Conclusión

phpDocumentor es una herramienta potente para crear documentación a partir de código fuente PHP. El formato de comentarios de documentación es fácil de aprender por ser muy parecido al formato de javadoc. (Ver lista de características de phpDocumentor.)

Una desventaja es que cuesta hacer phpDocumentor funcionar. Es un problema que desaparece una vez familiarizado con la herramienta, pero que puede causar frustración al principio. También existe una interfaz gráfica para generar la documentación pero que requiere un servidor web propiamente configurado. No está mal pero no quita el problema de tener que leer mucha documentación antes del primer éxito. En comparación el wizard de doxygen es bastante más fácil de usar.

Además, doxygen entiende varios lenguajes de programación, aunque no es capaz de ofrecer diferentes temas. Aún así vale la pena reflexionar sobre qué herramienta usar.

Referencias

  • phpDocumentor – la herramienta descrita en este artículo
  • Doxygen – una alternativa a phpDocumentor
  • javadoc – un sistema de documentación para Java.
  • PHP – el lenguaje de programación que phpDocumentor documenta.
  • DocBook – un formato para escribir textos sin especificar su aparencia y que phpDocumentor usa para la documentación libre (tutoriales).

El lenguaje PHP viene con un instalador que se integra bien con el servidor web Apache bajo un entorno Windows. Sin embargo, es posible que el servicio de Apache ya no arranca después de la instalación de PHP. Recibes errores como “PHP Warning: PHP Startup: Unable to load dynamic library” en el fichero error.log del servidor Apache o cuando ejecutas php -i.

Esto es obviamente un error de PHP y podemos esperar que no todas las versiones presentan este problema. Yo lo observé con PHP 5.2. Este problema se debe a que algunos componentes (librerías DLL) tienen incompatibilidades.

Por esto motivo conviene sólo instalar los paquetes que realmente se usan. Esto se puede hacer de dos formas:

  1. Cambiar la instalación de PHP: Ir al panel de control, agregar o quitar programas, y cambiar/quitar la instalación de PHP. En el diálogo del instalador deshabilitar todos los componentes que no se usan.
  2. Editar el fichero php.ini. Buscar la cadena “extenstion=” y comenta todos los modulos que no necesitas.  Procura editar el fichero php.ini en el directorio correcto. En la configuración del servidor Apache httpd.conf encuentras (normalmente al final del fichero) el atributo PHPIniDir que indica la ruta al directorio de PHP.

Después de cambiar la instalación de PHP debes rearrancar el servidor web para hacer efectivo los cambios.

No todos los módulos causan problemas, pero cuando menos quedan mejor. Sin embargo, también arriesgamos correr en el problema contrario. Si PHP muestra un error “Call to undefined function“, entonces no tenemos cargado el módulo necesario que contiene esta función.

Busca esta función en el manual de PHP y comprueba en qué módulo se encuentra. Cuando tienes la página de la función abierta pulsa el enlace “Up” (hacía arriba) para encontrar en la tabla de contenidos la sección de instalación. Puedes habilitar un módulo en las dos formas mencionadas arriba: por el instalador de Windows o descomentando la línea “extenstion=” correspondiente en el fichero php.ini.

En cada proyecto existe un flujo de trabajo, en se recurren de forma secuencial varias etapas.

  1. Se genera una idea general de que debe hacer el proyecto. Por ejemplo un programa que controla los movimientos de trenes en una estación.
  2. Los ingenieros de sistema elaboran con los clientes un cuaderno de carga y traducen los requisitos a un diseño. Por ejemplo:  El programa tiene una ventana en que se puede editar el horario del tren. Tiene un botón “Aceptar” que copia los datos en la ventana a la base de datos.
  3. Los programadores escriben un código que hace esto.
  4. Los ingenieros de prueba comparan el comportamiento del programa con los requisitos en el cuaderno de carga

¿Esto no suene totalmente lógico? Pensar qué quieres, cómo lo quieres, hacerlo y comprobar si lo has hecho bien. Lógicamente es un ideal que en una empresa de alta tecnología no se puede conseguir siempre. Por lo tanto, el ciclo de desarrollo suele tener típicamente más bien el siguiente estilo.

  1. El director general de los tuyos y de los suyos firman un contrato, donde aparece por primera vez una fecha de entrega.
  2. Los ingenieros de sistema comienzan a ingeniarse un sistema y avanzan a buen ritmo.
  3. El jefe de los ingenieros de sistema se da cuenta que el buen ritmo haría duplicar el tiempo previsto para esta fase y pide que se diseñen los componentes en dos en lugar de cuatro semanas.
  4. Como los ingenieros de sistema no llegan, los programadores empiezan a programar aunque todavía no está de todo claro qué.
  5. Los programadores avanzan a buen ritmo.
  6. A la medida que los ingenieros de sistema completan el diseño del programa, los programadores deben modificar el código.
  7. Los ingenieros de prueba comienzan a probar el programa que a veces falla porque las partes en cuestión aún no están programadas.
  8. Entre fijar los errores reportados por los ingeniero de prueba y modificar el diseño del programa hay tímidos intentos de adaptar el programa para hacerlo más legible, orientado a objetos, modular y tal. Por alguna coincidencia hay de repente dos días enteros para esto porque el programa ya contiene todo para la primera entrega parcial al cliente.
  9. El cliente está contento porque ve que están todas las ventanas aunque los botones no funcionan. Los ingenieros de sistema sugieren un cambio propuesto por los programadores de reducir el número de
    los botones “Aceptar” a uno por ventana.
  10. Los programadores lo cambian.
  11. Mientras añaden componentes adicionales a la funcionalidad, el cliente se muestra preocupado por el elevado número de errores detectados por los ingenieros de prueba. El jefe del programa promete reducirlo.
  12. Como el tiempo entre arreglar errores y programar nuevas partes con más errores se hace corto, el jefe del grupo prefiere aumentar la rapidez. Lo que se podría dividir en dos ficheros también cabe en uno, además a estas alturas del proyecto ya no conviene agrupar los ficheros en directorios por tener una mejor modularidad.
  13. El cliente se da cuenta que no se puede configurar la aplicación. Pide editores para que los parámetros fijos sean variables inicializadas con un fichero de texto.
  14. Los programadores empiezan a reescribir su código para que todos los arrays sean de un tamaño variable.
  15. El jefe del programa se muestra preocupado, porque que fallan partes del código que funcionaban antes.
  16. Los ingenieros de sistema adaptan su documentación de diseño a lo que realmente hay programado.
  17. El programa se cuelga tres veces en una prueba con el cliente. Esto tiene máxima prioridad y justifica que al final, sí, se dividen los ficheros en varios directorios.
  18. Aún falta un 20% de la funcionalidad. La lista de errores ha crecido porque todos los programadores estaban ocupados por los fallos fatales de la semana pasada. Al mismo tiempo, se ponen por lo menos tres errores por anomalía reportada para que el número de errores parezca más pequeño.
  19. Un programador detecta que la ventana de configuración debe tener opciones que son técnicamente imposibles de realizar. Después que el programador descubra como se podría hacer algo similar, el ingeniero de sistema propone que el programador le diga como lo haya programado para que pueda adaptar el documento de diseño.
  20. El programa se cuelga sin más razón aparente cuatro días antes de la prueba final. Las revisiones de los programadores durante el fin de semana fallan por la documentación incompleta de bibliotecas externas. El cliente no acepta el programa a pruebas.
  21. Tras haber codificado toda la funcionalidad, el cliente quiere que todos los mensajes de textos sean leídos desde un fichero. Realmente ya lo quería desde el principio, pero como esto no añade funcionalidad, nunca se hizo.
  22. Tres programadores se quedan con los errores no resolubles. Uno de ellos tiene también la tarea de inventarse los comentarios para que parezca que al menos un 30% de líneas sean comentarios.
  23. Supuestamente, el cliente usará el proyecto y no se puede excluir que también será documentado.

¿Qué conclusiones podemos sacar? Pues, la primera es ciertamente que hagas un repaso a tus ideales juveniles pero sin deprimirte. Aunque parece imposible de cambiar el mundo entero, sí, es posible que al menos tú hagas lo mejor posible.

Por ejemplo, el cambio que podrías enfocar desde ahora es dejar de quejarte de que la imprevisión e la improvisación son la regla (a pesar de lo que diga tu jefe), sino que se planifica en tiempo real. Suene muy bien y es una pieza para obtener la actitud adecuada para que algún día tú serás el jefe.

Te deseo mucha suerte.

En muchas ocasiones se pide un manual de referencia del código. Esto es especialmente importante a la hora de crear una librería que esté ideada para que otro programador la use en una aplicación final. Este manual engloba de alguna forma un trabajo doble: hay que explicar bien el funcionamiento de cada función en el código mismo y luego repetir la misma información en un documento de texto.

De este trabajo pueden encargarse sistemas de documentación automáticos. Leen el código y extraen la información necesaria para generar la documentación. Muchas veces permiten varios tipos de documentos de salida: en formato HTML, en formato PDF, como fichero de HTML comprimida (CHM) o en formato “Rich Text”.

Probablemente el más influyente de estos sistemas es javadoc. Fue incluido en el entorno Java de Sun como una parte integral del sistema y ha marcado desde entonces el formato de los comentarios de documentación. El programa de más uso es probablemente doxygen, ya que puede entender la sintaxis de muchos lenguajes, muy especialmente de C++. Para PHP es recomendable usar phpDocumentor.

A todos estos sistemas es común que requieren un marcador especial para los comentarios que se exportan del código a la documentación final. El más extendido es el de javadoc que marca los comentarios entre /** y */. Nota que es nada más que un comentario al estilo /**/ donde la primera letra es un asterisco. Sin embargo, para el sistema de documentación, el segundo asterisco en /** lo marca como un comentario de documentación cuyo contenido será procesado por el sistema de documentación automatizado.

Dentro de este bloque están permitido una multitud de etiquetas (tags en inglés) que dependen del objeto documentado, por ejemplo si es un fichero una clase o una función. La declaración de una función podría parecerse a algo como esto:

/**
 * La primera frase es un título/resumen de la función. Después del
 * punto que delimita la primera frase comienza la documentación
 * detallada. Características especiales están marcadas con una
 * etiqueta.
 * @param parametro El parámetro de la función.
 * @return La función devuelve un resultado.
 */
int mi_funcion(int parametro);

Guarda tus ficheros fuentes en Unicode si quieres tener acentos correctos en la documentación. Esto vale al menos para phpDocumentor.

Los sistemas de documentación sólo documentan declaraciones, objetos y tipos globales, ficheros y inclusiones. No documentan el cuerpo de una función ya que se supone que no es algo importante en un manual de referencia. Sin embargo ofrecen una etiqueta @internal con información interna, que sólo se compila con un cierto flag activado. De la misma forma se puede documentar o no variables y métodos privados.

Es cierto que los sistemas de documentación requieren algún esfuerzo al principio para instalar y entenderlos. Sin embargo, una vez superado esta fase ofrecen tener una documentación actualizada prácticamente en tiempo real que con sus enlaces y agrupaciones son una ayuda incluso para el autor durante el desarrollo. El entorno Eclipse ya incluye un enlace para mostrar la documentación de doxygen para código de C++.

¿Quieres curar un cáncer sin hacer nada? Vaya pregunta manipuladora, ¿verdad? Creo sería mejor preguntar ¿quieres curar un cáncer sin dejar de fumar? Pues, no es tan fácil, pero es relativamente fácil colaborar para que otros lo puedan.

Científicos suelen ser inteligentes pero un poco cortos en dinero (al menos en España). Hubo alguien con necesidad de supercomputadora pero poco presupuesto a quien ocurrió que se podría aprovechar de los tiempos sin uso de los ordenadores personales como el tuyo. Mientras tú no trabajas con tu ordenador, él trabaja para la ciencia.

Los autores dicen

Use el tiempo de inactividad de su ordenador (Windows, Mac, o Linux) para curar enfermedades, estudiar el calentamiento global, descubrir pulsares, y haga muchos otros tipos de investigación científica.

Captura de la ventana de BOINC Manager

El BOINC Manager te informa sobre la tarea actual. También existe una visión avanzada con más detalles y estadísticas.

El programa se llama BOINC, como Berkley Open Infrastructure for Network Computing, algo como la infraestructura abierta para la computación en red de la universidad de Berkley. Lo puedes descargar de la página boinc.berkeley.edu.

El funcionamiento del programa, una vez instalada, consiste básicamente en bajar una tarea de cálculo de la red, calcularla como una tarea de baja prioridad en tu ordenador, y devolver el resultado al remitente. Es decir, prácticamente tu ordenador hace mucho mientras tú no haces nada.

Casi nada. Debes apuntarte a un proyecto. Los proyectos son fuentes de tareas. La mayoría son de medicina y biológica, pero si los cánceres no te importan tanto, también hay bastante de proyectos de física, aunque desgraciadamente el LHC, el Large Hadron Collider, colisionador grande de hadronos no tiene nada que calcular todavía. El LHC es este superanillo que se ha montado cerca de Ginebra para generar las partículas más energéticas vistas en experimentos físicos hasta ahora. Algunos proyectos engloban varios programas. Por ejemplo, Ibercivis (en www.ibercivis.es) ofrece cálculos a entidades científicas españolas. Para alguien con una visión más allá está el programa SETI para buscar extraterrestre. Es posible apuntarse a varios proyectos.

Para apuntarse a un proyecto hay que registrase con un usuario y contraseña. No requiere ningún dato personal, es sólo que el servidor sabe distinguirte de los demás. Todos los proyectos tienen una página en la red donde te puedes informar sobre más detalles.

Algunos procesos en tu ordenador también se ejecutan con prioridad baja (como puede ser un escaneo del anti-virus) y pueden pararse debido a BOINC. Por eso puedes “dormitar” el proceso con el icono de la barra de tareas. BOINC continúa tras un tiempo parado. Si esto no te gusta puedes pararlo de todo.

En las preferencias puedes especificar más detalles como el uso de tu CPU y el de la red. Así puedes acumular tareas por la noche y ejecutarlas durante el día. Podría ser una solución para reducir el tráfico de red en horas pocas adecuadas como puede suceder en el trabajo.

En fin, BOINC conecta ciclos de CPU no usados con gente que los necesitan y ofrece la posibilidad de aumentar la potencia de cálculo de los científicos sin que ellos necesitan gastarse nuestros preciosos impuestos en ordenadores. Por el punto de vista de informática es un programa interesante a pesar de que está diseñado de llamar la menos atención posible en tu ordenador.

Nos puede tocar corregir alguna función que tiene 500 líneas, libre de comentarios y además con una sangría mal alineada. No creas que esta función ha sido obra de un genio que domina el caos. Ha sido el producto de muchos retoques de mucha gente y, además, no funciona. Por eso ahora te toca a ti.

Muchas veces me he sentido tonto por no entender una función larga, pero también es cierto que iba con desventaja: él que la hizo lógicamente sabía mejor que hubo ahí y luego, sólo te piden un pequeño retoque. Bueno, es posible que te toca este trabajo porque dentro lo que cabe eres él que menos tonto se presenta ante el problema. Así tranquilo. Si tú no entiendes funciones más largas que 30 líneas, otros las entienden aún mucho menos. Y como ya te toca, no preguntes si puedes modificar algo más de lo que te han pedido. Modifica la función para que por una vez sea comprensible.

¿Cómo hacemos esto? Pues, básicamente usamos dos técnicas a la vez.

  1. Siempre cuando hayamos entendido algo lo apuntamos como una nota en forma de un comentario en el código. Porque así guardamos lo que ya hemos entendido. Podemos ser detallado. Somos nosotros que debemos arreglar la función y aunque muchos no se molestan en poner comentarios, nadie se queja si están. (¿Quién aparte de ti se los leería?)
  2. No te cortes a renombrar variables con mal nombre si son locales en esta función. Compilándolo todo verás si esto funciona. Ten cuidado con esto en lenguajes interpretados ya que muchos permiten usar variables sin declarar. Olvidarse de cambiar el nombre de una variable en un sitio donde se usa puede causar que al final tienes dos variables distintos (y otro error más):
  3. En cuanto hayamos entendido una estructura, la exportamos como una subfunción y sustituimos este trozo por la llamada a esta nueva subfunción.

El tercer punto requiere más explicación: la idea fundamental es separar la función larga en un conjunto de subfunciones de manera que el código de ninguna de ellas ocupe más que una pantalla. (Es lo que yo soy capaz de entender.) Estas subfunciones requerirán parámetros. La meta es minimizar el número de estos parámetros.

Bloques internos – en muchos lenguajes marcados con llaves {} – suelen ser un buen candidato para convertirse en una subfunción. Por ejemplo puedes optar por

if (alguna expresión)
{
    mi_subfunción();
}
else
{
    mi_subfunción_else();
}

También puedes separar una estructura entera: un switch, un bucle o un if que a su vez llaman a más funciones dependientes. Es favorable que metes la estructura entera en una función para quede simétrica. No hagas cosas así

if (not mi_subfuncion_hace_algo_si_el_if_está_true())
{
    ejecuta_algo_con_else();
}

Antes de convertir un bloque en una subfunción debes averiguar cuales variables serán necesario. Idealmente declaras variables donde se necesitan, es decir mueve la declaración dentro del bloque si es posible. Estas serán variables locales de la nueva subfunción y no aparecerán en el interfaz.

Empieza por lo fácil. Reemplaza

if (a > b)
{
    c = a;
}
else
{
    c = b;
}

por

c = max(a, b);

A medida que cambias las cosas fáciles, las estructuras no tan fáciles se hacen más evidente. Crear una subfunción no es sólo escribir un código que hace lo mismo en otra forma. Puede aportar la información semántica que, como en nuestro ejemplo, se quiere usar el máximo de dos valores. Haciéndolo de esta forma es más probable que ves el bosque tras tantos árboles.

Cuando separas código piensa que una función puede seguir distintos pasos, por ejemplo inicialización, proceso, salida. Estos pueden ser funciones distintas que usan una estructura de datos como entrada y salida.

Idealmente tus funciones se parecen al índice de un libro. La función de máxima jierarquía contiene llamadas a los capítulos; estas a su vez son funciones que llaman a subcapítulos hasta el último nivel donde finalmente está el código realmente ejecutable. Como ejemplo ponemos tres niveles de jierarquía:

mi_funcion()
{
    // Llama a entrada, proceso, salida
    DatosEntrada entrada;
    lee_datos(datos);
    DatosSalida salida;
    procesa_datos(entrada, salida);
    muestra_salida(salida);
}

función lee_datos(DatosEntrada datos)
{
    // Llama a las distintas fuentes de entrada
    lee_datos_disco(datos);
    lee_datos_internet(datos);
    lee_datos_no_sé_donde(datos);
}

función lee_datos_disco(DatosEntrada datos)
{
    // Lee de una fuente en concreto
    FILE* f = fopen("fichero", "r");
    fread(&datos.numero, size(int), 1, f);
}

Cuando exportas funciones como miembro de una clase, decláralas privadas menos excepciones justificadas. Código duplicado debería estar en una subfunción siempre.

En la initialización puedes poner un retorno anticipado si no puedes seguir con los datos presentes. Mejor que muchos if añadidos sobre si está el fichero, si puedo leer datos etc., con un else perdido cien líneas más abajo, es algo como

if (no tengo fichero)
{
    return;
}
// Aquí se cumple la condición que tengo un fichero y
// puedo seguir sin más chequeo.

De esta forma lo que realmente quieres hacer en esta función no está añidado en el nivel cinco sino en la columna principal.

En fin, entender una función larga consiste en separarla en bloques comprensibles. Podemos intentarlo nada más que en la mente, pero si tenemos la posibilidad, podemos representar nuestra comprensión en el código – adaptándolo con una reestructuración y comentarios y lo que ya hemos hallado. De esta forma entendemos la función y la mejoramos a la vez.

Probablemente has aprendido en algún sitio que se ponen comentarios para documentar el código y explicar los pasos que está haciendo tu programa a otros. Esto no está de todo erróneo, aunque la vida real te puede enseñar otra cosa.

Comentarios no son productivos. No añaden funcionalidad que se podría vender, cuestan tiempo en escribirlos, se quedan obsoleto si cambias el programa y poner comentarios sobra si usas identificadores autoexplicativos. Si quieres saber qué pasa en cada línea del programa, usa un depurador y observa como se cambian las variables. Una de mis frases favoritas es: “Los comentarios ponemos cuando tenemos tiempo.” Desde luego nunca tenemos tiempo. Si no dímelo y te asigno otra tarea.

En conclusión: Olvídate poner comentarios para satisfacer a otros a menos que sea explícitamente requerido. Al final nadie se leerá más que dos líneas de comentario.

La verdadera razón de poner un comentario eres tú. Porque tú eres la primera persona en leer, entender y depurar todo este código que has hecho. Así elige: ¿Poner orden te da pereza y no te importa pasar horas sólo para hacerte entender otra vez lo que ya entendiste hace medio año o, al contrario, es el caos que te da pereza y prefieres que alguien explica para qué todo este código es útil?

No extraña que los programadores que más tendencia tienen a usar identificadores autoexplicativos son también aquellos que más comentarios ponen. Simplemente prefieren pensarse algo una vez y no cada vez que leen algo.

Un trozo de programa escribes una vez, lo modificas diez veces y lo lees cien veces. Si tardas 99 segundos en escribir un comentario que te ahorra un segundo de pensar a la hora de leerlo, entonces todavía sales rentable. Como es probable que nadie más se leerá tu comentario, tampoco cuesta mucho pensar cuántos detalles pones en el comentario: los que a ti te gustaría tener la próxima vez que debes entender esto.

Mis criterios son los siguientes:

  • Si escribo una nueva función, lo primero que hago es apuntarme lo que esta función debería hacer. Esto será el comentario de la cabecera.
  • Lo mismo vale dentro de una función. Primero escribo lo que quiero hacer ahí.
  • Aunque no estoy seguro si borraré una líneas al final, siempre pongo comentarios. Así tengo código bonito desde el primer momento y me ahorro la frustración que debo repasar todo si una vez funciona. (Al final borrar algo no cuesta tanto.)
  • Si hace falta cambiar código de forma temporal, pongo marcadores como “start debug maddin” y “end debug maddin”. Está claro que son cambios temporales y como no nombre variables “maddin”, es fácil encontrar todos los marcadores temporales. Si cada uno usa una palabra distinta en lugar de “maddin” es incluso posible saber quién ha dejado el marcador ahí.
  • Si una línea de código funciona con misterio pongo un largo comentario no sólo sobre lo que hace esta línea sino porque es mejor que otra forma.
  • A veces pongo incluso un comentario al estilo “me he pasado dos horas para descubrir que el programa se cuelga si llamo la variable a. Ahora se llama b por eso.” Relaja tus sentimientos agresivos cuando lo escribes y te hace sonreír cuando lo vuelves a encontrar. No te cortes incluir alguna palabrota si te relaja. Como mucho tu jefe sale del armario admitiendo que lee tu comentarios.

En fin, la próxima vez que te preguntas si pones un comentario o no, ponlo.

Si comparas las ofertas laborales para recién titulados con las de profesionales con experiencia, te das cuenta que experiencia laboral vale dinero. Mucho dinero.

¿Por qué esto es así? La informática se mueve deprisa y son los jóvenes que aportan los conocimientos de la última tecnología. Sin embargo, los técnicos se encuentran a menudo con problemas que no aparecen en ningún libro. Todo está correctamente programado, pero aún así el programa no funciona. Muchas veces el punto clave no está tanto en el saber como la intuición: un sentimiento sobre los altibajos de un ordenador que ayuda a localizar problemas en minutos donde un enfoque basado en la lógica tardaría días. Y es esta la diferencia entre un veterano y un novato.

No obstante, la intuición es también una forma de saber pero que se adquiere más por experiencia que por estudio: nuestro cerebro descubre patrones en el problema actual que se parecen a problemas pasados sin que nos demos cuenta. La inspiración a adivinar correctamente la causa de un problema es un don, pero podemos ayudarla usando una cierta metodología. No toda la experiencia se convierte en intuición. Mucha experiencia también aporta un saber adicional para situaciones particulares.

En este blog intento transmitir la experiencia de como encontrar y resolver errores o directamente evitarlo desde el principio. Lógicamente no puede ser una enciclopedia de todos los errores posibles, pero intenta afinar los sentidos para encontrar la solución.

Escribe tu dirección de correo electrónico para suscribirte a este blog, y recibir notificaciones de nuevos mensajes por correo.

Únete a otros 57 seguidores

Archivos

abril 2010
L M X J V S D
 1234
567891011
12131415161718
19202122232425
2627282930