[UCR]  
[/\]
Universidad de Costa Rica
Escuela de Ciencias de la
Computación e Informática
[<=] [home] [<>] [\/] [=>]
Google Translate

Convenciones de Programación para Pascal

Revisión 2.0

Adolfo Di Mare



Resumen [<>] [\/] [/\]

Presento un conjunto de convenciones para escribir programas en lenguaje Pascal, orientadas principalmente a la versión 5.0 del Turbo Pascal. El uso de estas convenciones ayuda a lograr una mejor legibilidad de los programas, y aumenta considerablemente la calidad en su documentación interna. I explain a set of conventions to write Pascal programs, oriented mainly to Turbo Pascal v5.0. The use of this conventions helps to achieve a better readability of programs, and their internal documentation is substantially improved.

1. Introducción [<>] [\/] [/\]

      Es frecuente en nuestro medio oir a estudiantes y profesionales de la programación confesar, no sin cierta preocupación, que no están en capacidad de documentar un programa, o de siquiera escribirlo bien. Aunque en algunos casos puede que el interlocutor realmente sea ignorante, en realidad lo que le falta es decorar el programa sistemáticamente, para que el resultado sea un buen programa. Una manera de lograrlo es siguiendo las convenciones de programación aquí descritas. Aunque estas convenciones son para Pascal, un programador ducho puede adaptarlas fácilmente a otros lenguajes.

      Estas convenciones de programación son reglas para hacer la documentación interna del programa. El seguirlas no garantiza que todo programa quede correctamente documentado, pero el programador cuidadoso logrará de ellas gran provecho. Después de todo representan la experiencia de muchos programadores, que hemos contribuido con diversos detalles, que juntos forman un todo bastante armonioso y útil.

      Estas convenciones no son las más agradables a primera vista. Sin embargo, después de un tiempo de usarlas, nuestro cerebro se acostumbra a ellas, y las acepta. Al tiempo, programas que no siguen las convenciones se ven mal.

      Adoptar estas convenciones es difícil, pues requiere mucho trabajo, con la desventaja de tener que sincronizar nuestro gusto con el de otros. Pero después de un corto tiempo se logra, y paradójicamente las convenciones se tornan más agradables de lo que uno quisiera aceptar. Además, si todos las usamos, todos estaremos más contentos compartiendo nuestros programas.

      Niklaus Wirth diseñó primero Pascal y luego su sucesor, Modula-2 [Wir-82]. Lo más importante de Modula-2 es que incorpora el concepto de módulo, y elimina la necesidad de usar el BEGIN para denotar un bloque. En estas convenciones se ha dado gran importancia a que un programa Pascal se parezca mucho a su equivalente en Modula-2, con lo que se logra servir a programadores en ambos lenguajes (y posiblemente también a quienes prefieran Oberon [Wir-88], el sucesor de Modula-2). Por ejemplo, en estas convenciones las palabras claves deben escribirse en mayúscula, que es la única forma aceptable para Modula-2.

      Este documento toma en cuenta todas las propuestas para convenciones de programación presentadas por mis estudiantes y también otros criterios encontrados en diferentes libros y artículos. Además, para cada convención doy la justificación que ayudará al programador a aceptarla. La práctica demostrará las debilidades y aciertos de estos criterios para escribir programas, y con el tiempo tendremos un estándar realmente sólido que podremos usar sin temores.


2. Documentación de sistemas [<>] [\/] [/\]

      Un sistema es un conjunto de programas integrados en un objetivo común. En general la documentación del sistema depende mucho de la aplicación implementada. Un posible índice para la documentación de un sistema es el siguiente:

Introducción Arquitectura del sistema
Objetivos del sistema Detalles de implementación
Requerimientos Glosario e índices
Guía de uso del sistema

      La definición formal de cada uno de estos puntos es extensa, y puede encontrarse en la mayoría de los libros de Ingeniería de Sistemas. Buenos ejemplos de sistemas bien documentados son los manuales de compiladores, como el Turbo Pascal 5.5 de Borland [BI-88].

      En este artículo me concentro en la documentación de programas, y no de sistemas, por lo que ahora trato en detalle cómo hacerlo. En la ECCI se han dado varios esfuerzos para mejorar este importante aspecto de la programación, y yo he usado por base los de José Ronald Argüello y Ricardo Villalón.


3. Documentación de programas [<>] [\/] [/\]

      La documentación de programas tiene dos objetivos claramente definidos: primero lograr que un usuario del programa pueda usarlo, y segundo lograr que entender el funcionamiento del programa sea fácil, de forma que el programa pueda ser cambiado o mejorado con un mínimo de esfuerzo.

      A este fin, todos los buenos consejos de nuestros profesores de Redacción y Ortografía de secundaria son válidos: la exposición de ideas en la documentación debe ser clara, concisa, sencilla, válida, exacta. Además, debe usarse el lenguaje adecuadamente, evitando ambigüedades.

      Escribir la documentación de un programa es un gran esfuerzo intelectual. Escribir no es fácil, y en general las personas lo hacen mal. Por eso lo usual es producir documentaciones pobres, mediocres o pésimas. Debemos cambiar nuestra actitud hacia la documentación, que en realidad es necesaria para que el producto de nuestro trabajo esté terminado: un programa sin documentación es un auto sin manubrio; un programa sin documentación no es un programa, es una adefesio.

      Es en general cierto que los programadores derivan placer del programar, no del documentar. Además, parece que lo contrario es también cierto: a los programadores les duele escribir la documentación, hasta tal punto que la dejan para el final. Lo cierto es que la documentación es parte de la programación, independiente del estado anímico del programador. Cada programador debe aprender a escribir la especificación de su programa (o sea, la documentación), antes de escribir el programa.

      Esta actitud negativa hacia el documentar se deriva de un estilo corrupto de programación, promovido por tratar de escribir el programa a la mayor celeridad. Se cree que lo único importante es que el programa funcione, y se relega a un segundo plano todo lo demás. Si al no documentar se logra en algunos casos terminar más rápidamente el programa, en realidad se hace mucho más difícil su mantenimiento posterior, pues la mayoría de los programas deben ser modificados muchas veces, antes de ser desechados.

      Si un programa se construye siguiendo las sanas prácticas de diseño modular y programación estructurada, es entonces necesario definir primero qué debe hacer cada parte del programa, mediante una especificación, para luego escribir cómo se logra. Esto es prueba de que el engorro que representa al programador el escribir documentación se deriva de su ignorancia de las técnicas de programación usadas por los buenos profesionales, y no de que sea desagradable escribirla: antes de construir un programa es necesario definir cuál debe ser su comportamiento. Es triste que muchos programadores no diseñen sus programas: el resultado es un sistema "feo".

      Además, conforme se codifica cada algoritmo, el programador puede hacer pequeñas anotaciones en el programa, que aumentan su legibilidad. No es necesario hacer más. Quiere esto decir que la documentación (interna) del programa se escribe de manera natural con el programa, en dos pasos: primero al definir las especificaciones de cada procedimiento o tipo abstracto de datos, y luego al concretar cada uno de los algoritmos. Hacer más que esto es innecesario, e irrelevante.

      Desde una perspectiva de alto nivel, al no documentar inicialmente un programa adecuadamente, lo que se hace es traspasar el costo de la documentación al futuro, cuando deben hacerse modificaciones. Pero si no existe documentación, para cada modificación debe estudiarse profundamente el funcionamiento del programa, de hecho redescubriendo todo lo no documentado, con la desventaja adicional de generalmente quien modifica el programa no es el mismo que lo escribió en primera instancia. Y cada nueva modificación implica de nuevo un esfuerzo intelectual, que pudo evitarse al documentar bien el programa desde el principio.

      O sea, siempre es necesario documentar, y lo mejor (y más barato) es hacerlo cuando se está escribiendo el programa. No después, cuando ya se han olvidado los detalles relevantes, o las decisiones de impacto que se tomaron al concretar la implementación.

      Si un programa tiene más de 250 líneas (que es lo usual), el programador deberá trabajar en él varias horas, sino varios días. El documentar su programa mientras lo escribe le evita recordar todos los detalles de su implementación, y le obliga a definir y aclarar sus ideas. Si una persona no puede hablar (escribir) concretamente sobre algo, menos podrá programarlo. La prueba de fuego es lograr que otro entienda lo que uno ha producido: ¿se atreve usted ha tomarla en cada uno de sus programas?

      Además, como todo buen escritor, un programador debe revisar el código que ha escrito una y otra vez. En cada nueva revisión encontrará nuevas maneras de documentar su programa, y podrá corregir las imprecisiones en que haya incurrido. La programación es una proceso iterativo, y es necio tratar de lograr un resultado correcto al primer intento.

      Pasemos ahora a examinar en detalle lo que debemos incluir en nuestra documentación. La documentación del programa en general se divide en dos partes: interna y externa. La interna es la que acompaña al código fuente, y todo lo demás es la externa. Aunque es posible prescindir de la documentación externa por completo, en general la costumbre es mantener la documentación interna simple y concisa. Cuando es necesario hacer grandes aclaraciones en prosa o por medio de dibujos, es mejor incluirlas en la documentación externa.

      Un teórico de la programación podría argumentar que la diferencia entre documentación interna y externa no existe en realidad, pues en la práctica lo que debe hacerse es escribir una jerarquía de documentos que describen un producto de logical. En la práctica cada programa o sistema tiene requerimientos específicos, y no es fácil usar siempre el mismo patrón de documentación.

      Por eso, el sagaz programador deberá incluir tantos niveles descriptivos, o de abstracción, como sean necesarios para lograr una buena documentación. No debe poner más de lo suficiente, ni menos de lo necesario. Sobre todo, debe producir un conjunto de documentos que le satisfagan.

      Como corolario de lo anterior se desprende que un buen método para evaluar la documentación producida, es contestar la pregunta: ¿Si me entregaran a mí esta documentación para que yo modificara el programa, sería suficiente? Obviamente un mejor método para evaluar la documentación es lograr que un segundo la analice, a la luz de esta misma pregunta. Todo aquello que él no pueda resolver fácilmente a partir de lo documentado merece ser revisado y mejorado, hasta lograr un documento que, valga la redundancia, si documente.

      Es bueno escribir los programas para el mundo, no para "yo". Tal vez el sentirnos expuestos a la crítica de los demás nos ayude a producir programas de más calidad.

4. Documentación externa [<>] [\/] [/\]

      Es redundante decirlo, pero esta documentación debe estar escrita en papel, aunque puede haber sido producida por medio del computador. En ella se describen los siguientes puntos:

  1. Descripción del problema a resolver: En un ambiente universitario esto puede reducirse a incluir una fotocopia de la descripción dada por el profesor del trabajo a realizar. En general el programador debe definir el objetivo de su programa, no sea que termine programando otra cosa. De esta manera al escribir su programa tiene una mejor idea del trabajo a realizar.
  2. Planteamiento del problema: En este aparte debe describirse la solución empleada. El programa es una expresión detallada del método de solución, por lo que este planteo debe hacerse en un alto nivel de abstracción (o en varios, si fuera necesario). El objetivo es lograr que el lector comprenda rápidamente el enfoque usado para lograr la implementación del programa.
          No es prohibido usar tablas, gráficas o cualquier otro artilugio que aumente la calidad del documento producido. "Un dibujo vale por mil palabras..."
  3. Definición de módulos e interfaces: Como el programa está compuesto de partes que interactúan en una forma ordenada, es necesario que todas estas relaciones queden claramente especificadas. En este aparte es conveniente incluir diagramas ilustrativos, y jerarquías de llamados entre módulos.
  4. Análisis de la implementación: Sólo el programador conoce los intríngulis de su programa, y debe compartirlo con quienes en el futuro estudien su trabajo.
  5. Descripción de archivos usados: Esta incluye no sólo definir cada campo almacenado, sino también las estructuras e interrelaciones de los archivos, y sus peculiaridades. En el "jet set" de la programación, éstos se conocen como Bases de Datos.
  6. Descripción de entradas y salidas: El trabajo realizado por un programa se percibe por sus resultados. Estos deben quedar claramente definidos en esta sección, así como los insumos necesarios para producirlos.
  7. Requerimientos especiales: En general cada programa impone restricciones al problema que puede resolver, o soluciona un problema parcialmente. Los alcances del programa deben quedar claramente delimitados.
  8. Manual de uso del programa: Contrario a las creencias de los programadores, los programas no funcionan solos, pues deben ser corridos por operadores. Y ellos necesitan instructivos completos. El instructivo debe contestar las preguntas del usuario: debe suplir al programador.
          Pregúntese el programador: ¿Contiene el manual de uso las respuestas a todas las preguntas que surgirán a un usuario? ¿Será necesario alguna vez llamar al programador a media noche cuando un operador esté lidiando con el programa? Si lo fuera, no debería serlo.
  9. Descripción de datos de prueba: Este aparte sirve para agrupar todos los casos de prueba que se usaron para verificar el correcto funcionamiento del programa. Desgraciadamente es común omitir esta sección de la documentación, tal vez porque los seres humanos nos avergonzamos de que nuestros programas tengan errores, y tratamos de ocultar todo aquello que pueda demostrarlo.
          Pero sabemos que construir programas correctos es un proceso iterativo. Nadie lo logra al primer intento, por lo que debemos aceptar que el encontrar un error es un signo no de nuestra incompetencia, sino más bien de que el programa se acerca a estar terminado.
          El incluir los datos de prueba ayuda a demostrar que el programa funciona correctamente, fomenta la programación disciplinada y no desordenada, y evita el rediseñar todo los casos de prueba cada vez que el programa se modifica. En ambientes universitarios al profesor le resulta más fácil corregir el trabajo, lo que en general redunda en una mejor nota.
  10. Glosario e índices: Es inaceptable omitir el glosario o los índices, pues se obliga al lector a recorrer toda la documentación cada vez que necesita consultar algún detalle. Aunque es difícil hacer este aparte, las buenas nuevas son que la mayoría de los procesadores de palabras serios (como WordPerfect) ayudan mucho en esto.

      Los anteriores diez puntos son, no por casualidad, una copia un poco detallada de los mencionados para documentar sistemas. Esta analogía no es casual, pues en realidad un sistema puede verse como un "programote".


Documentación de programas

  1. Descripción del problema a resolver
  2. Planteamiento del problema
  3. Definición de módulos e interfaces
  4. Análisis de la implementación
  5. Descripción de archivos usados

  1. Descripción de entradas y salidas
  2. Requerimientos especiales
  3. Manual de uso del programa
  4. Descripción de datos de prueba
  5. Glosario e índices

5. Documentación interna [<>] [\/] [/\]

      Ahora ya podemos hablar de cómo debe verse el código del programa. Pero antes de definir en detalle cómo debe escribirse cada estructura del lenguaje, es necesario aclarar tres puntos. Primero, al examinar un programa cualquiera, el lector debe experimentar una sensación agradable, una invitación a estudiar en detalle el código escrito.

      O sea, que el programa debe verse bonito. Organizado. Ordenado. Fácil de leer. Claro.

      Dicho de otra manera: Si se ve feo, es que está mal hecho.

      Segundo, el objetivo de la documentación interna es facilitar el posterior entendimiento del programa. No basta que el código se vea bonito, sino que además debe estar bien escrito: "Aunque la mona se vista de seda, mona se queda".

      Tercero, si hay trabajo que el compilador puede hacer, entonces debemos dejarle hacerlo. Esta es la principal razón para justificar la verificación de tipos que Pascal y otros lenguajes hacen. Un programador que siente su expresividad totalmente coartada por esto, debe reflexionar seriamente, pues el costo de correr el compilador es mucho más bajo que el de mantenerle encontrando errores que la máquina pudo detectar.


6. Identificadores [<>] [\/] [/\]

      Todos sabemos que los identificadores usados en el programa deben ser expresivos, claros, definitivamente relevantes a la solución del problema de marras, y por si fuera poco, legibles. Muy legibles.

      Los nombres de las constantes, tipos y variables deben ser nombres significativos o nemónicos. Esto con el fin de que el programador los recuerde fácilmente cuando programa, y para que quien lea el programa comprenda la función de determinadas variables, tipos o constantes.

      Para lograr estas cualidades el programador debe escoger concienzudamente cada identificador. Cuando no pueda hacerlo, deberá aceptar que todavía no entiende la solución de su problema, pues ni siquiera es capaz de hablar correctamente acerca de él. Usar identificadores inadecuados es prueba contundente de que la implementación no va por buen camino, y en general predice el fracaso del proyecto.

      Una costumbre que hemos heredado de los tiempos del Fortran es usar i, j y k como índices siempre. Si aceptamos que los nombres de identificadores deben ser significativos, entonces también lo deben ser los nombres de índices: en lugar de escribir semana[i], lo correcto es escribir semana[dia].

      Una vez escogido un identificador adecuado, puede incorporarse dentro del nombre elegido información adicional sobre el uso del identificador. Por ejemplo, si el identificador es un tipo nuevo definido por el programador, entonces al incluirle el prefijo T el lector sabrá de ese hecho. La siguiente tabla lista algunas de estas convenciones:

Ejemplo Identificador definido, y su uso
cont Variable, pues comienza con minúscula
variable_larga "_" separa las palabras del identificador
variableLarga Otro estilo, que usa un caracter menos
vListAlfSymTel hay que abreviar si es necesario hacerlo
Blanco Constante, pues comienza con una mayúscula
TPersona Tipo usado para definir variables de personas
PPersona Puntero a una variable de tipo PPersona
Corrector(a,b) Un procedimiento comienza en mayúscula

      Estas convenciones son consistentes. Se usa un sufijo para determinar la calidad del identificador:

Prefijo Significado
T tipos
P tipo puntero

      Hay que destacar que cuando se usan varias palabras para formar un identificador, éstas deben ser muy legibles: esto_si_seVeBien, peroestocasiquenoseentiende. ¿O si?

      Los únicos identificadores que comienzan con una minúscula son las variables; todos los demás identificadores que no pueden cambiar comienzan con una mayúscula: constantes, tipos, procedimientos, funciones, etc.

      Un caso particular de esta regla es que los nombres de los procedimientos estándar Pascal deben escribirse con una mayúscula al principio, que es el estilo usado por la mayoría de los programadores profesionales: ReadLn(), WriteLn(), ClrScr(), Halt(). Recordemos que estos procedimientos no forman parte del lenguaje, hasta tal punto que pueden ser redefinidos por el programador; si no fuera esto así escribiríamos sus nombres en mayúsculas.

      Algunos programadores se resisten a usar letras minúsculas en sus programas, y escriben todo en mayúscula (esta práctica era necesaria hace 20 años, cuando no había minúsculas). Con esto lo que logran es hacer menos legibles sus identificadores, pues estamos acostumbrados a leer en minúscula, usando las mayúsculas sólo en casos especiales.

      El programador no debe restringir el tamaño de un identificador, sino más bien debe escogerlo para que sea significativo. Sin embargo, ante la posibilidad de escoger entre uno largo y otro corto, deberá escogerse el corto si tiene la misma expresividad del largo. Si queremos denotar la altura de una persona, es mejor usar el identificador alt que altura_persona, siempre y cuando alt sea suficientemente claro en el contexto de programación. En general es mejor usar el caracter "_" paraSepararPalabrasEnUn identificador, pues
      resulta_en_identificadores_más_legibles.

      Tal vez sea importante mencionar que la mayoría de los ligadores de eslabonamiento modernos manejan identificadores de hasta 32 caracteres, por lo que ése parece un número que puede usarse como longitud máxima de un identificador. Identificadores muy largos son signo de mala programación.

      Algunas variables tienen un significado predefinido en la literatura, por lo que sus nombres deben usarse en ese mismo sentido. Por ejemplo, en casi todos los libros de matemática la letra i se usa casi siempre como índice, y lo general es que x sea el parámetro de una función de variable real. Es entonces contraproducente usar una en lugar de la otra:

Lo feo
Lo bonito
sum := 0; I := 6.23194;
FOR X := 1 TO y DO BEGIN
  n   := n / F(X*I);
  sum := sum + a[X] - n;
END;
x    := 6.23194;
suma := 0;
FOR i := 1 TO n DO BEGIN
  y    := y / f(i*x);
  suma := suma + A[i] - y;
END;

      Como puede notarse en este ejemplo, a veces es conveniente no seguir las convenciones: f(x) se ve mejor que F(x), pues estamos acostumbrados a leer en los textos la primera forma. Lo mismo podemos decir de la referencia al Arreglo A, pues al escribir su nombre con mayúscula se nota mejor el sentido del programa. Un fanático de las convenciones diría que debemos usar una "a" minúscula, pero ¿verdad que se ve mejor con A "grandota"? El fanatismo documentalista puede ser contraproducente.

      También es una muy buena práctica el usar constantes en lugar de valores literales, pues entonces el programa tiene más sentido para el lector, y si es necesario cambiar el valor del literal puede hacerse en un sólo lugar, en vez de tener que recorrer todo el programa y sus módulos buscando los literales que deben ser cambiados.


7. Constantes [<>] [\/] [/\]

      Es muy importante usar constantes. Su uso evita que el texto del programa esté plagado de números y tiras que muchas veces no tienen significado. Veamos un ejemplo:

Lo feo
Lo bonito
pago := total * 1.1 * 1.1;
pago :=
  total * Imp_Ventas * Propina;

      Al usar constantes el texto del programa es más claro. Además, es posible cambiar el valor de la constante en todo el programa con sólo modificar una línea del programa. En el ejemplo anterior, si sucediera que la propina pasa a ser el 15%, el programador debería examinar con sumo cuidado cada línea del programa en que el literal 1.1 aparece, para decidir si debe o no cambiarlo. Si hubiera usado constantes desde el principio, todo ese trabajo se habría evitado.

      Pero además las constantes sirven para relacionar unas estructuras de datos con otras, usualmente al usar arreglos. Veamos:

Lo feo
Lo bonito


VAR
  a : ARRAY [1..23] OF CHAR
  b : ARRAY [1..45] OF CHAR
CONST
  LgNom = 23; { Largo del nombre }
VAR
  a : ARRAY [1..LgNom]     OF CHAR;
  b : ARRAY [1..2*LgNom-1] OF CHAR;

      Desgraciadamente el usar expresiones en declaraciones no es válido en las versiones ANSI de Pascal, pero sí lo es en Modula-2, C y Turbo Pascal. El uso de constantes en este contexto aclara mucho la relación que existe entre las variables y estructuras de datos de un programa.

      Usar constantes es tan necesario para producir buenos programas, que podemos insultar a los malos lenguajes definiéndolos así: "Un lenguaje es malo si no permite usar constantes. Es pésimo si no permite usar procedimientos que tomen argumentos."


8. Palabras Reservadas [<>] [\/] [/\]

      Todo lenguaje cuenta con varias palabras clave, que denotan las diferentes estructuras sintácticas permitidas: DO, BEGIN, WHILE, etc. Aunque no es claro cuál es la forma más "mejor" para escribirlas, nosotros sólo usaremos mayúsculas. Existen tres convenciones que dividen a la humanidad en este respecto: los que abogan por usar sólo minúsculas, los de las mayúsculas, y los indecisos que las escriben con la primera letra mayúscula, y el resto en minúscula.

      El programador experto notará que en el lenguaje Modula-2 las palabras clave se escriben en mayúscula. A diferencia del Pascal, los compiladores para Modula-2 si son sensitivos a las mayúsculas y minúsculas, por lo que es buena idea copiar las convenciones usadas en este otro lenguaje, que es un super-Pascal. Entonces todos los identificadores que representan tipos predefinidos deben escribirse también usando mayúsculas: INTEGER, REAL, BOOLEAN, CHAR, etc. De esta manera nuestros programas Pascal se parecerán mucho a programas Modula-2, y será más fácil traducirlos a ese nuevo lenguaje.

      La mejor razón para usar mayúsculas es que resaltan cada palabra clave. De hecho, los libros de texto sofisticados usan letra negrilla para destacar las palabras clave. Pero como al listar en la pantalla un programa con un editor de texto no es posible (en la mayoría de los computadores) obtener este gracioso efecto, debemos conformarnos con usar mayúsculas. Esta no es la regla usada en Ada o C, pues en esos lenguajes se usan minúsculas para las palabras reservadas.

      La desventaja de usar mayúsculas es que debemos apretar la tecla de mayúsculas muchas veces, pues las palabras reservadas son las más en un programa. Esto tiene remedio si usamos un editor con capacidad para definir plantillas (macros). Para cada construcción sintáctica del lenguaje (IF, CASE, FOR, etc.) podemos definir una plantilla que invocamos con una corta secuencia de teclas, y como producto obtenemos una plantilla completa de la instrucción respectiva. Por ejemplo, en una IBM/pc usando el programa SMACS (o SuperKey, o ProKey), al pulsar la secuencia Shift-Alt-I se puede obtener:

IF _ THEN BEGIN
END
ELSE BEGIN
END;

      La enorme ventaja de usar plantillas no es sólo el salvar unos cuantos teclazos, sino también obtener siempre construcciones sintácticamente válidas. Su gran desventaja es que es necesario aprender a usar los macros del editor. Pero es un precio pequeño comparado con el beneficio obtenido.


9. Comentarios [<>] [\/] [/\]

      Es difícil indicar exactamente cuándo incluir comentarios en el programa. Sin embargo, todos los comentarios deben cumplir con lo siguiente:

c1) Deben ser completos c4) Deben ser claros
c2) Deben ser válidos c5) Deben ser coherentes
c3) Deben ser pertinentes c6) etc.

      Obviamente, los comentarios deben ser suficientes para permitirle al lector entender lo que el programa hace. A veces es innecesario incluir comentarios, pues el código mismo es suficientemente explicativo, pero en general esto no es cierto. El programador siempre ve unos cuantos detalles que no son obvios, pues necesitan de un poco de razonamiento para ser deducidos del código del programa.

      El programador bisoño siempre se pregunta cuál es la ocasión indicada para incluir un comentario. La obvia respuesta es, invariablemente, YA.

      Si esperamos a después para incluirlos, nos encontraremos con que hemos olvidado lo que íbamos a escribir. O sea, que debemos escribir el comentario precisamente en el momento en que sospechamos que debemos hacerlo. Dejarlo para después es sinónimo de no documentar nuestro programa. No incluir comentarios en un programa es El Pecado Capital de la Programación.

      Cuando nos percatamos de que un comentario sobra, podemos borrarlo. Es mucho más facil borrar lo mal escrito, que escribir lo no escrito.

      En el 1250 AC Sócrates dijo: "Un programa sin comentarios no es un programa, sino una secuencia de símbolos inconexos que un compilador traduce a lenguaje de objeto. Un programa sin documentación no es un programa, es un problema. Un programa sin documentación no es un programa, es un buen método para perder y dinero, y tiempo, y hígado, y jugos gástricos, y glucogelamina, y eso, y más. Un programa sin documentación no es un programa, es el paraíso del loco, la doctrina del incompetente, y el relamer del imbécil"[1].

      "Un programa sin documentación no es un programa, es un insulto al programador. Un programa sin documentación no es un programa, es una nada, un sinfín de noes, una blasfemia, una declaración de guerra... Un programa sin documentación no es un programa, es[2]...

      En cuanto a la forma de los comentarios, es conveniente que estén alineados unos con otros. Si están dentro de un bloque, deben estar adecuadamente indentados, y siempre que sea posible debe dejarse una espacio separando a las llaves del comentario. Deben usarse (* y *) sólo en contadas ocasiones, pues las llaves ocupan menos espacio. Las llaves que enmarcan a un comentario deben estar alineadas. Por ejemplo:

VAR
  a,b : INTEGER;      { factores de conversión }
  d_a : REAL;         { diferencial en a       }

      A veces el programador sobredocumenta sus programas, al decir cosas que ya están dichas:

INC(a);   { incrementa a }

      En este caso el comentario sobra, y no ayuda a la legibilidad del programa. Debe ser eliminado.


10. Instrucciones [<>] [\/] [/\]

      En general es una buena práctica escribir una instrucción por renglón. De esta manera, puede usarse el margen derecho remanente para incluir comentarios. Sólo en casos excepcionales deben incluirse dos o tres instrucciones en un sólo renglón, si el hacerlo incrementa mucho la legibilidad del programa. Toda instrucción debe terminarse con punto y coma (;).

      Además, la mayoría de los compiladores y depuradores simbólicos se refieren al texto por el número de línea, y no por el de instrucción. Si cada instrucción está en una línea, los mensajes de error serán más claros, y será más sencillo depurar el programa. ¿Para qué ahorrar espacios?

      El deseo de atiborrar todas las instrucciones en una sóla línea nace a veces de la necesidad de ver todo el código de un procedimiento en la pantalla. Como esto sucede bastante, es mejor que el programador saque un listado del procedimiento en cuestión. Definitivamente las cosas en papel se ven mejor que en la pantalla, aún cuando la pantalla tenga 100 líneas de capacidad.

      En muy pocos casos es más claro poner varias asignaciones en el mismo renglón, principalmente antes de un ciclo WHILE o REPEAT, o cuando deben inicializarse vectores o matrices:

{ Define los valores iniciales de a y b }
a[1,1] := 1.0;  a[1,2] := 4.0; a[1,3] := 23.0;
a[2,1] := 0.0;  a[2,2] := 6.0; a[2,3] :=  2.0;

b[1,1] := 1.0;  b[1,2] := 0.0; b[1,3] := 12.0;
b[2,1] := 0.0;  b[2,2] := 2.0; b[2,3] :=  4.0;

{ Ciclo de conversión }
i := 0; j := Limite;
WHILE (i<=Limite) AND NOT Eof(Mat_F) DO BEGIN
  i := i+1;  j := j-1;

  ReadLn(Mat_F, d[j,i]);
  b[j,i]     :=  i*j  * d[i,j];
  c[i+j,i-j] := (j-i) * d[j,i];
END;

      Muchos programadores se ahorran el punto y coma al final de una instrucción, cuando aparece al final de un bloque. Aunque esto reduce mínimamente la cantidad de teclazos necesarios para escribir el programa, cuando se inserta una instrucción debe volverse al renglón anterior a poner el punto y coma. Al hacer pequeños cambios esto no es problema, pero cuando se deben mover grandes bloques, o hay que insertar muchas instrucciones, entonces pueden producirse errores incómodos de encontrar. Es mejor usar el punto y coma como delimitador, y no como separador. Además, con el punto y coma al final, cada instrucción parece más completa.

      Por ningún motivo debe escribirse después de la columna 80. Recordemos que los programas son estudiados, depurados y mejorados en pantallas con 80 columnas, y las instrucciones que tienen más caracteres no pueden verse completas. Lo mismo sucede con las impresoras, que imprimen 80 columnas cuando se usan las fuentes no comprimidas.

      El sagaz programador agrupará las instrucciones relacionadas en bloques separados por blancos. Los comentarios generales deben aparacer, debidamente indentados, al principio del bloque:

{ Proceso de todos los rangos identificados }
FOR i := 2 TO n DO BEGIN
  { Primero se inicializan las estructuras  }
  Init_rangos(a, b, n*(i-1) );
  Init_local (c, a, b[i-1]);

  { Ahoras procese cada parte }
  Ampliar_rango(a,b);
  Incluir_rango(c,b[i]);     { sólo el acumulado }
END;

Convertidor(a, b,
  Rango_limite, matriz_cambio,
  c[Rango_limite]);

      En muchas ocasiones es necesario usar varias líneas para una sóla instrucción. Por ejemplo, si un procedimiento tiene muchos argumentos, o si se desea destacar que algunos están lógicamente agrupados, pueden necesitarse varias líneas para la instrucción. En estos casos lo adecuado es indentar las líneas de continuación.


11. Pragmas [<>] [\/] [/\]

      Un pragma es una instrucción al compilador, para seleccionar alguna opción. Los pragmas deben incluir un comentario que explique la opción que representa:

{$B-  Fuerza evaluación booleana de corto circuito          }
{$R-  No verifica rangos de índices en tiempo de ejecución  }
{$S+  Verifica que no se produzca un sobrerebase de la pila }

12. Declaraciones [<>] [\/] [/\]

      La declaración introduce una variable por primera vez al lector. Lo razonable es que sea en ese momento que se le informe también de su utilidad en el programa. La declaración de una variable no está completa sin un comentario que justifique su existencia.

      El programador sagaz también tratará de agrupar las definiciones de variables relacionadas, resaltando el hecho de que deben usarse en conjunto. Este mismo principio debe aplicarse al definir procedimientos, tipos y constantes. ¿No es ésto lo que en realidad hacemos al definir un tipo abstracto de datos?

      Nuestro cerebro aprecia la uniformidad, por lo que es bueno alinear en las declaraciones los separadores y signos de puntuación:

Lo feo
Lo bonito
CONST
  PI = 3.14159;
  Epsilon = 1.0 e-10;
  Delta = 12 e -32;
  LgNom = 23;
CONST
  PI      =  3.14159;
  Epsilon =  1.0 e-10;
  Delta   = 12.0 e-32;
  LgNom   = 23;

      Es conveniente declarar cada variable en un renglón aparte, para facilitar el incluirle comentarios. Los dos puntos deben estar alineados:

Lo feo
Lo bonito
VAR
  alfa:   integer;
  b:   real;
VAR
  alfa : INTEGER;
  b    : REAL;

13. Expresiones [<>] [\/] [/\]

      La mayoría de las instrucciones de un programa la constituyen expresiones. Es realmente difícil definir reglas para escribir expresiones, pues las mismas pueden ser arbitrariamente complejas. Lo correcto entonces es usar algunas reglas generales, por no demás ambiguas, para dar formato a las expresiones. Lo importante es que el programador quede satisfecho con la forma en que ha escrito su expresión, y que use paréntesis cuando sea necesario. Las reglas son las siguientes:

e1) Deben usarse paréntesis en caso de duda
e2) en expresiones "grandes" deben usarse blancos
e3) en expresiones "pequeñas" no deben usarse blancos
e4) no deben usarse blancos alrededor de paréntesis
e5) en general, deben usarse blancos alrededor de < > =
e6) no simpre puede caber todo en un sólo renglón
e7) si todavía se ve feo, arréglelo de nuevo

      Unos ejemplos aclararán estos puntos:

a := (b AND c) OR ((i+j+k > 12) AND (a)) ((b<c) OR (c<d));
h := b < (c+3);
c := interes + Inversion(a*b/3, Balance(Mes(hoy), 88));

      En general, cuando una variable consta de una sola letra, la expresión se ve mejor si no se usan blancos alrededor de los operadores aritméticos y de comparación. Cuando el identificador tiene muchas letras, el ponerle blancos alrededor lo adorna.

      Cuando una expresión ocupa varios renglones, es válido (aunque incómodo) indentar de forma que la continuación esté alineada con el punto de corte sintáctico del nivel anterior. Por ejemplo:

pasada := (Abs(randx[randx[11]]) MOD 128 + 1
           + randx[1] / 65536.0) / 128.0;

w      := y +  ((((y * p4 + p3) * y + p2) * y + p1) * y + p0)
             / ((((y * q4 + q3) * y + q2) * y + q1) * y + q0)

      Este tipo de expresiones son comunes en programas científicos. Lo importante en estos casos es que se sepa a qué nivel de anidamiento de paréntesis se corta el renglón. En general es mejor no usar expresiones tan complejas, salvo en los casos que sea realmente necesario.

      Es también muy saludable dejar un espacio alrededor de los símbolos ":=", ":" y "=", pues se logra una mayor legibilidad. Es increíble que algunos deseen ahorrar "bytes" no incluyendo suficientes espacios blancos para separar las partes de una instrucción.


14. Indentación [<>] [\/] [/\]

      Al indentar su código, el programador usa espacios para alinear las sentencias del programa de manera que la estructura del programa es fácilmente identificable.

      La indentación definitivamente es uno de los factores más importantes para lograr mejores programas. Se han realizado muchos estudios "científicos" al respecto, pero la experiencia del lector es la prueba más convincente de la necesidad de indentar adecuadamente. Si usted no lo cree, trate de entender los programas Basic escritos en muchas revistas de aficionados a la computación.

      Al indentar un programa adecuadamente se logra destacar su estructura de control. De hecho, es más fácil entender un programa indentado adecuadamente, que los correspondientes diagramas de flujo. Dado lo difícil que es dibujar un diagrama de flujo aún con herramientas automáticas, y lo poco claros que resultan (pues ocultan el anidamiento de las estructuras de control del programa), es mejor nunca usarlos. La indentación les sustituye completamente en casi todos los casos. Por todo esto, una máxima de todo programador debería ser:

 No indentar es Sacrilegio en programación

      Es difícil resaltar la importancia real de la indentación de programas, pero tal vez un ejemplo exagerado logre el impacto necesario:

Lo feo
Lo bonito
  FOR i := 1 TO n
DO
   BEGIN
  Read(a[i]);
IF b<c THEN a[i] := 0 ELSE
a[i+1] := i-1; END
FOR i := 1 TO n DO BEGIN
  Read(a[i]);
  IF b<c THEN BEGIN
    a[i]   := 0;
  END
  ELSE BEGIN
    a[i+1] := i-1;
  END;
END;

      Debe indentarse dos (2) espacios cada vez que se utilice una construcción anidada. Siempre debe indentarse una construcción anidada, y siempre debe indentarse en dos espacios. La consistencia en la aplicación de esta regla permite a nuestro cerebro predecir la forma del programa, y a la postre hace la lectura más sencilla. También debe indentarse el bloque BEGIN-END que forma el cuerpo de un procedimiento o programa.

      Tal vez tres sería un factor de indentación mejor que dos. Tal vez lo sería cuatro, o cinco. La verdad es que la mayoría de los programadores profesionales usan sólo dos espacios al indentar. Esto es suficiente, y de paso se lograr ahorrar espacio a la derecha de las instrucciones, que es donde se escriben muchos de los comentarios. Pruebe el lector con diferentes factores de indentación, pero recuerde que todos los demás usaremos consistentemente el mismo número mágico: dos.

      Muchas de las construcciones anidadas del Pascal terminan con la palabra clave END. Esta deberá quedar a la misma altura que la palabra inicial del bloque, salvo en los casos en que explícitamente se diga lo contrario. Por ejemplo, cuando se define un registro, la palabra clave END debe estar alineada con el identificador del registro:

CONST
  Lg_Nombre =  35;   { largo del nombre de una persona física }
  Lg_Matriz =  22;   { tamaño de una matriz de transacción    }
  Lg_Tira   = 255;

TYPE
  TColor = (         { colores del arcoiris válidos   }
    Azul,
    Verde,           { los colores NO se ordenan por  }
    Amarillo,        { su valor espectral             }
    Rosa,
    Lila,
    Magenta
  ); { TColor }      { Los nombres están alineados... }

  TNombre  = ARRAY [1..Lg_Nombre] OF Char;
  PPersona = ^TPersona;
  TPersona = RECORD          { sólo personas físicas       }
    nombre : TNombre;        { nombre propio de la persona }
    edad   : INTEGER;        { en años cumplidos           }
    sexo   : TSexo;
    cualidades  : RECORD
      fumador,
      lector,
      perezoso :  BOOLEAN;
    END;
    estatura : REAL;         { en metros con 3 decimales   }
    ojos     : TColor;       { color de los ojos           }
  END; { TPersona }

      La palabra clave END sirve para alinear el programa, y retornar al orden de indentación anterior a la construcción anidada. Además, cuando se sale del anidamiento y es relevante hacerlo, debe incluirse el identificador de la construcción respectiva (como en el caso del fin del registro TPersona).

      Del ejemplo podemos apreciar que END no es la única palabra que termina un bloque de indentación: el paréntesis cerrado u otras palabras clave también pueden cumplir ese propósito. En general, cuando una palabra clave termina un bloque de indentación, debe estar alineada con el comienzo del bloque, y todo el bloque delimitado debe estar indentado dos espacios.

      Hay que observar que un programador poco exigente se conformaría con incluir menos comentarios en el ejemplo anterior, aduciendo, por ejemplo, que el significado del vocablo "TColor" es obvio. Sin embargo, al escribirlo le evitamos al lector tener que pensar, y por ende le ayudamos a entender más fácilmente, que es nuestro objetivo al documentar.

      Recordemos que lo obvio para el programador no lo es para el lector, quien le está leyendo por primera vez su programa. No está el lector en la misma posición del programador, quien ya es un experto en el código escrito.

      Cada construcción Pascal estructurada debe indentarse siguiendo el patrón definido en la sección sobre indentación. Sin embargo, mas adelante definimos el formato para indentar cada una de las instrucciones del lenguaje.

      Muy relacionado al tema de la indentación es el de la alineación. Las estructuras del programa que están alineadas se ven mejor:

Lo feo
Lo bonito
cambio := 1;
d := 2;
rh := 3;
delta := 23;
cambio :=  1;
d      :=  2;
rh     :=  3;
delta  := 23;

      Del ejemplo puede observarse como la alineación de las asignaciones hace más legible el programa. Es factible alinear no sólo asignaciones, sino también los tipos en declaraciones (como se hizo para el registro TPersona). En general los comentarios se ven mejor cuando las llaves que los encierran están alineados (observe el ejemplo anterior en que se define el registro TPersona).


15. Indentación del IF [<>] [\/] [/\]

      Para el IF debe escribirse el THEN en el mismo renglón, y el ELSE debe estar alineado al IF. Además, el BEGIN debe seguir al THEN o al ELSE cuando sea necesario (ahorrando de esta manera un renglón, y dos espacios de indentación):

IF expr_1 OR expr_2 THEN BEGIN
  Cabeza(a,b);
  Cola(b);
END
ELSE BEGIN
  WriteLn('Imposible procesar');
  Halt;
END;

      Cuando la expresión es muy grande, entonces el THEN BEGIN debe ser claramente visible:

IF expr_1 AND NOT (expr_2 OR (i<j))
  AND ( (j>k) OR (i<=l) )
  OR (NOT found AND expr_3)         THEN BEGIN
  Cabeza(a,b);
  Cola(a);
END;

      Aunque lo anterior se ve un poco raro, la razón principal de ello es que las expresiones booleanas no deben ser muy complejas, pues entonces es muy difícil depurarlas.

      Una forma alternativa es alinea el THEN BEGIN con el IF:

IF expr_1 AND NOT (expr_2 OR (i<j))
  AND ( (j>k) OR (i<=l) )
  OR (NOT found AND expr_3)
THEN BEGIN
  Cabeza(a,b);
  Cola(a);
END;

      En el IF existe un caso especial, que se da cuando el bloque de instrucciones a ejecutar consta de una sóla instrucción:

Lo malo
Lo bueno
IF a>b THEN Cabeza(a,b)
ELSE Cabeza(b,a);
IF a>b THEN BEGIN
  Cabeza(a,b);
END
ELSE BEGIN
  Cabeza(b,a);
END;

      Algunos purista tratarán de ahorrar cuatro renglones al escribir toda la instrucción sin indentación, pero el resultado es desagradable. Es mejor preservar la estructura de indentación en todo el programa. Y aunque parezca un poco cargado el usar seis líneas para escribir esta secuencia, lo cierto es que en Modula-2 se usan cinco renglones. Al establecer la convención de incluir un bloque BEGIN-END para el IF siempre, se logra acercar más el Pascal a Modula-2, y aunque el precio a pagar es un poco alto (un renglón por instrucción IF), la consistencia en el uso de esta regla produce programas mucho más legibles, principalmente cuando se usan bloques IF muy extensos, o cuando hay mucho anidamiento.


16. Indentación del FOR [<>] [\/] [/\]

      Como en caso del IF, el DO BEGIN del FOR debe estar en el mismo renglón:

FOR i := 1 TO n DO BEGIN
  Read(a[i]);
  IF a[i] > max THEN BEGIN
    max := a[i];
  END;
END;

      Como ya se ha dicho, el END cierra el bloque del ciclo. Al usar un ciclo FOR siempre debe incluirse un bloque BEGIN-END, pues ayuda mucho a delimitar el código:

Lo malo
Lo bueno
FOR i := 1 TO n DO
  FOR j := 1 TO i-1 DO
    a[i,j] := 0;
FOR i := 1 TO n DO BEGIN
  FOR j := 1 TO i-1 DO BEGIN
    a[i,j] := 0;
  END;
END;

      El incluir los bloques BEGIN-END ayuda a delimitar el ámbito de cada ciclo, y evita confusiones. Además, al incluirlos es fácil incluir nuevas instrucciones en el bloque, mientras que en el otro caso es posible que al hacerlo olvidemos crear el bloque, y resulte un programa que, aunque indentado, funciona diferente a lo esperado. (Cada programador Pascal sabe que buscar un END perdido es un grave problema).

      Es una muy buena práctica el hacer el cuerpo completo de cada estructura de bloques, antes de rellenarla. Por ejemplo, el ejemplo de arriba debió ser digitado en la siguiente secuencia:

Bloque primero
Bloque segundo
FOR i := 1 TO n DO BEGIN
END;
FOR i := 1 TO n DO BEGIN
  FOR j := 1 TO i-1 DO BEGIN
  END;
END;

      Al seguir esta pequeña técnica, el programador mantiene en todo momento una estructura sintáctica válida, y evita tediosos errores de bloques que comienzan y no terminan, o viceversa. (Este mismo resultado se puede lograr usando los macros del editor).


17. Indentación del WHILE [<>] [\/] [/\]

      El WHILE debe indentarse siguiendo los mismos principios usados al indentar el FOR: debe siempre incluírsele un bloque BEGIN-END, y debe indentarse dos espacios:

i := 1; salir := FALSE;
WHILE (i<n) AND (NOT salir) DO BEGIN
  Read(a[i]);
  salir := (a[i] = Fin_Archivo);
  INC(i);
END;

18. Indentación del REPEAT [<>] [\/] [/\]

      El REPEAT debe indentarse de la misma forma que el WHILE y el FOR. A diferencia de éstos, no es necesario rodear el bloque de instrucciones a ejecutar por la pareja BEGIN-END, pues la palabra clave UNTIL sirve de delimitador:

i := 1;
REPEAT
  Read(a[i]);
  IF a[i] < max THEN BEGIN
    max := a[i];
  END;
  INC(i);
UNTIL i<n ;

19. Indentación del CASE [<>] [\/] [/\]

      Al escribir el CASE deben indentarse los bloque correspondientes a cada escogencia, y los valores de selección deben estar indentados respecto a la palabra clave CASE. El ELSE del CASE no se indenta. Además el END que cierra el CASE debe llevar entre llaves la palabra CASE:

FOR i := 1 TO n DO BEGIN
  Read(c);
  CASE c OF
    'a': BEGIN    { comando de inclusión  }
      Arreglar(ax,dy);
      Cambiar(dy,dz);
    END;
    'b': BEGIN    { comando de grabación  }
      Botar(dy);
      Recuperar(dz);
    END;
  ELSE BEGIN      { recuperar el anterior }
      Recuperar(dy);
      Cerrar(dy);
    END;
  END; { CASE }
END; { FOR }

      Lo usual al usar el CASE es escribir muchas selecciones relacionadas, por lo que deben usarse muchas instrucciones, que generalmente no caben en una sóla pantalla u hoja de papel. Por eso es conveniente que la estructura de la instrucción se refleje en la indentación. Esa es la misma razón que justifica el incluir la etiqueta { CASE } con el END que cierra el bloque. Por la misma razón se incluye la palabra clave { FOR }.

      Finalmente, la convención debe seguirse aún cuando cada bloque en el CASE tenga un renglón:

CASE i OF
  1: WriteLn('Avanzado');
  2: WriteLn('Intermedio');
  3: Writeln('Novato');
ELSE
  WriteLn('Inválido');
END; { CASE }

20. Selecciones Múltiples ELSE IF [<>] [\/] [/\]

      Una secuencia de IFs anidados pueden usarse para implementar una escogencia múltiple, en que las condiciones no son sencillas. Para estos casos, los IFs deben indentarse siguiendo el mismo patrón definido para el CASE y el IF: cada condición no se indentada, el END cierra un bloque de indentación, cada ELSE IF está en un nuevo renglón y el END que cierra el último ELSE IF contiene entre corchetes esas palabras clave:

FOR i := 1 TO n DO BEGIN
  Read(c);
  IF NOT c IN [Alfa, Beta] THEN BEGIN         { inclusión  }
    Arreglar(ax,dy);
    Cambiar(dy,dz);
  END
  ELSE IF Func_plot(c)-3 < Corte_h THEN BEGIN { grabación  }
    Botar(dy);
    Recuperar(dz);
  END
  ELSE BEGIN                                  { recuperar  }
    Recuperar(dy);
    Cerrar(dy);
  END; { ELSE IF }
END; { FOR }

21. Indentación del WITH [<>] [\/] [/\]

      Nicklaus Wirth no incluyó el WITH en Oberon, el paso siguiente en la evolución de Pascal, pues esta instrucción ayuda a confundir mucho en los programas. El WITH no ayuda a la legibilidad de un programa. La convención sobre el uso del WITH es:

 No debe usarse el WITH

      El problema principal con el WITH es que no es fácil para el lector saber si una variable es un campo de un registro o no. Cuando hay que dar mantenimiento a un programa un poco complejo, termina el programador saltando de procedimiento en procedimiento, buscando los campos de los registros. Y lo peor es que es muy difícil quitar las instrucciones WITH, más si están anidadas.


22. Procedimientos [<>] [\/] [/\]

      Después de la programación estructurada, el gran principio que permite construir grandes sistemas y programas es el uso de procedimientos (el siguiente paso es la programación por objetos). Es entonces necesario definir el formato en que éstos deben ser escritos.

      Un procedimiento siempre debe ser una abstracción, o sea que debe realizar algún trabajo, ocultando el detalle de proceso. En general, es bueno que a cada procedimiento corresponda una tarea específica, y que la cumpla a cabalidad. O sea, un procedimiento no debe ser incompleto, ni debe realizar muchas cosas no conexas, que no tienen gran cohesión en el contexto del programa.

      Además, un procedimiento debe ser "corto". Cuando una bloque de código se hace muy largo es difícil entender su funcionamiento, indentarlo y comentarlo. Es incómodo manipular grandes trozos de código, por lo que el código debe dividirse en pedazos coherentes, en que cada pedazo corresponde a un procedimiento.

      Un procedimiento está compuesto por varias partes. Las siguientes deben ser descritas:

p1) Nombre
p2) Parámetros
p3) Variables globales
p4) Especificación
p5) Implementación

      El nombre del procedimiento debe seguir las convenciones sobre identificadores descritas anteriormente.

      Para cada parámetro del procedimiento debe describirse si es de entrada, salida o ambos, y el propósito de su existencia. Lo mismo debe hacerse con las variables globales (aunque la costumbre de los buenos programadores es no utilizar jamás variables globales, aún cuando se necesitan).

      Cada parámetro debe escribirse en un renglón aparte, y debe estar seguido por un comentario descriptivo. Los parámetros deben agruparse en una secuencia lógica, que facilite el uso e identificación por parte de un usuario del procedimiento.

      La especificación del procedimiento describe el objetivo del procedimiento, y las condiciones bajo las que el procedimiento produce resultados correctos. Esta descripción debe ser independiente de la implementación pues de otra manera el procedimiento no sería una abstracción.

      Dentro de la especificación del procedimiento deben describirse bajo la cláusula "RESULTADO" el trabajo realizado por el procedimiento. A veces es difícil decir qué hace el procedimiento; eso sucede cuando el procedimiento no es una abstracción, por lo que debe ser revisado y corregido.

      En la cláusula "REQUIERE" las condiciones que las variables y parámetros deben cumplir para que el procedimiento funcione. El programador puede incluir otras cláusulas, como su nombre al principio del procedimiento y "MODIFICACIONES", en que se describen la fecha en que diversas modificaciones al procedimiento se han hecho. Esta última puede incluirse en el encabezado del procedimiento. La idea es que la especificación del procedimiento sea la página del manual de uso del mismo.

      Bajo el encabezado de "NOTAS" el programador puede describir cualquier detalle relevante que desee. El encabezado "EJEMPLO" se usa para dar un ejemplo de uso del procedimiento, y debe estar al principio del procedimiento.

      Pareciera ser que la documentación interna queda muy cargada, pero en realidad lo que sucede es que se está haciendo, para cada procedimiento, un manual de uso. En algunos casos, puede ser más conveniente incluir todas estas cláusulas en la documentación externa; lo erróneo es simplemente omitirlas. Lo cómodo de incluirlas dentro del programa es que se hacen precisamente cuando el procedimiento se define o modifica, con lo que la documentación siempre estará actualizada.

      El nombre del procedimiento debe incluirse como un comentario después del BEGIN que comienza el procedimiento, y también después del END que lo termina. Esto es particularmente útil cuando se usan procedimientos anidados. Niklaus Wirth, en su nuevo lenguaje Modula-2, ha considerado esta práctica tan importante que la ha hecho obligatoria en el END que cierra el bloque.

      También debe especificarse si el procedimiento es o no público, mediante la palabra clave "EXPORT". O sea, debe definirse cuáles procedimientos estarán accesibles al usuario.

      Mediante las unidades de Turbo Pascal es posible definir una biblioteca de procedimientos (UNIT), que contiene dos partes: interfaz e implementación. En la primera se incluyen sólo las declaraciones de procedimientos que pueden ser usados por los usuarios de la biblioteca, mientras que en la segunda se deben incluir todos los procedimientos, inclusive los no visibles. Al incluir la declaración "EXPORT" para los procedimientos que aparecen en la parte de interfaz de la unidad se resalta a los procedimientos públicos, y también permite el automatizar la creación de la sección de interfaz de la biblioteca. (En otros lenguajes, como C y Modula-2, existen facilidades diferentes para lograr este mismo propósito). Veamos un ejemplo completo:

FUNCTION Busqueda_Binaria( { EXPORT }    { Adolfo Di Mare  }
  {+} VAR a : Int_A;    { arreglo en el que se busca k     }
  {+}     l,            { posición desde en a              }
  {+}     h : TIndex,   { posición hasta en a              }
  {+}     k : INTEGER   { llave a buscar                   }
) {>>>>>>>} : TIndex;   { posición en la que se encontró k }
{ RESULTADO
  Esta función busca en el arreglo a la llave k. Si k está
  entonces retorna un índice p tal que a[p] := k. Si no es
  así, retorna Indice_Invalido                             }
{ REQUIERE
  a esta ordenado: a[i] <= a[j] siempre que i < j.
  El arreglo a tiene valores válidos desde a[l] hasta a[h] }
{ NOTAS
  Tomado del algoritmo de Niklaus Wirth: Algoritmos y
  Estructuras de datos, pag 64.                            }
{ EJEMPLO
  i := Busqueda_Binaria( arreglo, 1,n, llave)              }
{ MODIFICACIONES
  12-May-88: Ahora encuentra la última llave igual a k     }
VAR
  i,p : TIndex;
BEGIN { Busqueda_Binaria }
  p := Indice_Invalido;     { inicializa }
  FOR i := l TO h DO BEGIN
    IF a[i] = k THEN BEGIN  { PARCHAR }
      p := i;      { es secuencial.... NO binaria!!! }
    END;
  END;
  Busqueda_Binaria := p;
  { Después será programada la búsqueda binaria, pues
    ahora no tengo tiempo: ADH 12-May-88                  }
END; { Busqueda_Binaria }

      De este ejemplo pueden desprenderse varias cosas. La implementación no es una búsqueda binaria, como se había declarado originalmente. Sin embargo, la implementación escogida tiene todas las propiedades de la búsqueda binaria, salvo que es (mucho) más lenta. Esto es válido hacerlo, principalmente cuando se está desarrollando el programa. Nótese el comentario al respecto.

      Los comentarios se incluyen a todo lo largo del procedimiento, en aquellos lugares en que son pertinentes. Las llaves que encierran a los comentarios están alineadas, aunque esto implique "desperdiciar" algunos espacios en blanco. Esto es particularmente importante, pues da una sensación de orden a todo el programa, y lo hace agradable a la lectura.

      Las palabras "REQUIERE", "MODIFICACIONES", etc están cada una en un renglón aparte, y comienzan con mayúscula. Además, la llave que cierra el comentario de cada una de estas cláusulas está alineado, y se encuentra en el último renglón de la cláusula. Los comentarios que les siguen están alineados a cada palabra, e indentados. El tipo retornado por la función está indicado con el símbolo {>>>>>>}, que lo resalta.

      A la par de la declaración del procedimiento también se define si el procedimiento es o no público, y el nombre del programador.

      Cada parámetro aparece en un renglón, con una anotación: el {+} indica que el parámetro es de entrada, por lo que no puede ser modificado (o sea, que no puede estar a la izquierda de asignación alguna, ni puede aparecer como argumento modificable en procedimiento alguno). La anotación {-} indica que el parámetro es de salida. Un parámetro de salida debe siempre estar acompañado de la declaración VAR, pues de lo contrario es pasado por valor. Finalmente, la anotación {?} indica que el parámetro es de entrada y salida, o sea, que el procedimiento lo modifica. La tabla siguiente resume estas anotaciones:

   Anotación       uso del parámetro       declaración VAR   
{+} entrada opcional
{-} salida obligatoria
{?} entrada/salida obligatoria

      Cabe aclarar que hay muchas otras cosas que pueden mencionarse al definir un procedimiento, aunque nuestras convenciones son bastante completas. Por ejemplo, los manuales de Borland usan un formato parecido al aquí definido, y siempre incluyen un ejemplo de uso del procedimiento.

Al invocar a un procedimiento, en general es mejor separar los argumentos con un espacio en blanco, y no sólo con una coma. Pero si el procedimiento tiene pocos argumentos cortos, puede omitirse el espacio entre ellos:

Corrector(examen_final, nota, promedio);
a := f(x,y,z);

23. Unidades [<>] [\/] [/\]

      Para implementar la facilidad de compilación separada, el Turbo Pascal hace uso de unidades. La documentación de éstas es simple, pues únicamente deben incluirse algunos comentarios en el encabezado que describan el propósito de la unidad, el nombre del programador, y algunas otras dependencias o detalles de implementación relevantes.

      Debe copiarse en la sección de implementación el encabezado completo de cada procedimiento exportado. De esta manera es fácil examinar el encabezado de cada procedimiento en esta sección de la unidad.

      En algunas ocaciones pueden ser beneficioso usar alguna convención para nombrar unidadades, pero en general lo que realmente resulta útil es escoger para nombres de unidades identificadores que sean muy significativos.

      Para generar la sección de interfaz de una unidad automáticamente puede usarse el siguiente truco: escribir un programa que lea la sección de implementación de una unidad, y que copie en un archivo de salida solamente aquellos encabezados que tienen la anotación "EXPORT", descrita en el aparte de procedimientos. Usando tal programa el programador no debe preocuparse de que la interfaz y la implementación coincidan, con lo que su trabajo se facilita.

      Tal vez es importante acotar que el programador debe crear la sección de interfaz primero, especificando así cada parte de su programa. El método mecánico sugerido en el párrafo anterior simplemente evita digitar nuevamente la declaración de procedimientos. O sea, que el programador puede trabajar desde el principio en la sección de implementación de su programa, pero debe primero definir la especificación de cada procedimiento, y luego puede generar automáticamente la sección de interfaz de la unidad. Cada programador aprende trucos que le ayudan en su trabajo, y éste es sólo uno más de ellos. (No hay que jalarle mucho el chancho a la raba).


24. ADTs [<>] [\/] [/\]

      Un ADT es un tipo abstracto de datos, esto es, un tipo de datos que tienen varias operaciones asociadas. El acceso a una variable de ese tipo se permite únicamente a través de sus operaciones. El ejemplo clásico de ADT es la pila, con sus operaciones Push(), Pop() y Empty():

UNIT Stack;
INTERFACE
USES Elem;
TYPE
   Rep = {...} { representación privada del ADT }
   TStack = ARRAY[1..Sizeof(Rep)] OF BYTE;
PROCEDURE Push  ( {?} VAR s: Stack_ADT; {+} VAR e: Elem_ADT);
PROCEDURE Pop   ( {?} VAR s: Stack_ADT; {-} VAR e: Elem_ADT);
FUNCTION  Empty ( {+} VAR s: Stack_ADT) : {>>>>>>} BOOLEAN;
{...}
END.

      En una publicación aparte, se discute en detalle cómo implementar ADTs en Pascal [DiM-89]. Baste decir lo siguiente:


25. Uso de Variables Globales [<>] [\/] [/\]

      El uso de globales en general es una mala práctica, pues cambiando esas variables es posible hacer que un mismo procedimiento tenga comportarmientos diferentes cuando se le invoca con los mismos argumentos. O sea, al usar globales se quiebra el principio de que un procedimiento es una abstracción.

      Además, al usar globales se inhibe la verificación de tipos del compilador, por lo que errores que pudieron descubrirse al compilar el programa deben ser detectados manualmente por el programador.

      Pero en la práctica es en muchas ocasiones necesario usar variables globales, por lo que ofrecemos dos trucos para controlar su uso. De esta manera no las excluímos de nuestras técnicas, pero las usamos de forma que sepamos en todo momento que una variable global está en uso, evitando inhibir la verificación de tipos que tanto nos ayuda al programar.

      El primero y más seguro método es evitar el usarlas del todo, creando un procedimiento principal que sustituye al programa principal, en el que se definen todas las variables a usar en el programa. Al hacer esto es necesario enviar como argumentos todos los valores a los procedimientos usados en el programa, que puede resultar ineficiente en algunos casos. De todas formas, lo correcto es primero lograr la eficacia, y luego la eficiencia:

PROGRAM No_Globales;

PROCEDURE Uno(
  {+} a,b: INTEGER
);
BEGIN { Uno }
END; { Uno }

PROCEDURE TRABAJE;
{ Aquí comienza el programa principal }
VAR
  v_1, v_2 : INTEGER;
BEGIN { TRABAJE }
  Uno(v_1, v_2);
END; { TRABAJE }

BEGIN TRABAJE END. { No_Globales  }

      Al usar este pequeño truco, nos obligamos a pasar a todo procedimiento todos los argumentos que necesite para producir resultados, de hecho desencadenando el poderoso mecanismo de chequeo de tipos con que Pascal cuenta. Así el compilador encontrará en tiempo de compilación errores sintácticos que de otra manera deberíamos descubrir como errores de lógica. Este es un ejemplo en que el compilador trabaja para nosotros, y no nosotros para el compilador.

      El segundo truco es declarar todas las variables globales como campos de un registro llamado GLOBAL. En el siguiente ejemplo, todavía usamos el procedimiento TRABAJE() para no definir globales, pero ahora explícitamente debemos cualificar todas las variables globales con el nombre del registro que les contiene.

PROGRAM No_Globales;
VAR
  GLOBAL : RECORD
    hora_hoy        : INTEGER;
    archivo_entrada : FILE;
  END; { GLOBAL }

PROCEDURE TRABAJE;
{ Aquí comienza el programa principal }
VAR
  v_1, v_2 : INTEGER;
  f        : TEXT;
BEGIN { TRABAJE }
  v_1 := GLOBAL.hora_hoy;
Assign(f, GLOBAL.archivo_entrada);
. . .
. . .
END; { TRABAJE }

BEGIN TRABAJE END. { No_Globales  }

26. El programa principal [<>] [\/] [/\]

      Realmente el programa principal es un procedimiento, por lo que debe describirse de la manera en se describe un procedimiento. Además, es necesario incluir una descripción general del objetivo del programa, su forma de uso, restricciones, y requerimientos para entrada y salida. Es preocupante que la mayoría de los programadores ni siquiera escriban su nombre en el código fuente del programa principal.


27. Ejemplo [<>] [\/] [/\]

      En el siguiente programa se ilustran muchas de las convenciones discutidas. También se violan algunas, pero siempre para dar mayor legibilidad al código.

{$B+ Fuerza evaluación completa de expresiones booleanas }
{$R+ Fuerza verificación de rangos                       }

PROGRAM Hash (INPUT,OUTPUT);
{ Este programa lee el archivo 'enteros' que contiene números
  enteros, y cuenta cuantas veces aparece cada número.
- El programa procesa un número por línea. }
{ REQUIERE
  El archivo de entrada NO debe contener más de H_tamano
  números diferentes. }
{ METODO
  Se usa una tabla de dispersión para almacenar los diferentes
  números leídos, y luego se imprime la tabla secuencialmente }
{ PROGRAMADOR   Adolfo Di Mare  <adolfo@di-mare.com> }

USES
  Crt, Dos;
CONST
  H_tamano = 100;    { Número máximo de números de entrada    }
TYPE
  TTuple = RECORD    { Una entrada en la tabla de dispersión  }
    key,             { entero llave                           }
    cont : INTEGER;  { número de veces que está en el archivo }
  END; { TTuple }
  TTabla = ARRAY[1..H_tamano] OF TTuple;  { tabla de dispersión }

FUNCTION H(
  {+} k : INTEGER  { llave a insertar en la tabla de dispersión }
) {>>>} : INTEGER; { valor de la función de dispersión para k   }
BEGIN { H }
  H := (k MOD H_tamano) + 1;
END; { H }

PROCEDURE Int_set(
  {?} VAR tabla : TTabla;   { tabla hash                }
  {+}     key   : INTEGER   { llave a insertar en tabla }
);
{ RESULTADO
  Mediante la función de dispersión h se incluye en la tabla
  la llave key. Si ya estaba ahí, entonces se incrementa el
  contador respectivo                                       }
{ REQUIERE
  Acepta hasta H_tamano llaves diferentes. Ignora las demás }
{ NOTAS
  Los sinónimos se manejan usando direccionamiento abierto  }
{ MODIFICACIONES
  25-may-88 : ADH  logre que fuera el último, y no el 1ero  }
VAR
  comienzo,       { lugar en donde se empieza a buscar key  }
  i         : 1..H_tamano;
  cont      : INTEGER;
BEGIN { Int_set }
  comienzo := H(key);   { calcula el lugar para almacenar key }
  i        := comienzo;
  cont     := 0;        { para romper el ciclo de busqueda }

  REPEAT
    IF (tabla[i].key = 0) OR (tabla[i].key = key) THEN BEGIN
      tabla[i].key  := key;        { key ya está en la tabla }
      tabla[i].cont := tabla[i].cont + 1;
      cont          := tabla[i].cont;
    END
    ELSE BEGIN                    { busque en donde poner key }
       i := (i + 1) MOD H_tamano;
    END;
  UNTIL (cont <> 0) OR (i = comienzo);
END; { Int_set }

PROCEDURE TRABAJE;  { Programa principal }
VAR
  i     : INTEGER;
  tabla : TTabla;      { de dispersión      }
  g     : TEXT;        { archivo de entrada }
  key,
  cont  : INTEGER;

BEGIN { TRABAJE }
  FOR i := 1 TO H_tamano DO BEGIN
    tabla[i].cont := 0;      { inicializa la tabla en ceros }
  END;

  Assign(g,'enteros');
  Reset(g);
  WHILE NOT Eof(g) DO BEGIN
    ReadLn(g, key);       { Cada entero viene en un renglón }
    Int_set(tabla,key);   { inserta lo leído en la tabla    }
  END;
  { Imprime aquellos valores del archivo de entrada }
  FOR i := 1 TO H_tamano DO BEGIN
    IF tabla[i].cont <> 0 THEN BEGIN
      WriteLn(tabla[i].key:6, tabla[i].cont:6);
    END;
  END;
END; { TRABAJE }

BEGIN TRABAJE; END. { Hash }

28. Conclusiones [<>] [\/] [/\]

      Una buena documentación sólo se logra si se escribe con el programa, lo que requiere de una modesta cantidad de disciplina. Esta se logra con la práctica, y con el deseo de lograr buenos resultados en nuestro trabajo. Es muy importante que antes de escribir un procedimiento, el programador escriba su especificación. El ver la documentación como algo "extra" al programa es un error, que generalemnte tiene un alto valor.

      La documentación debe escribirse cuando se escribe el programa. Al seguir estas convenciones el resultado será, en todo momento, un programa sintácticamente correcto, y claramente escrito.

      Al documentar correctamente lograremos un mejor programa, simplemente porque un programa sin documentación no es un programa completo.

      Cada programador debe ser el juez de su propio trabajo. Estas convenciones serán agradables a unos, y malas para otros. Lo importante es que se usen convenciones; no hacerlo incide en la calidad de los programas producidos.


Agradecimientos [<>] [\/] [/\]

      Mis colegas en la UCR me han apoyado en el uso de estas convenciones, en especial mi colega y esposa Norma Ortega. Agradezco también el apoyo de la profesora Josefina Pujol, quien continúa usando mis convenciones en sus cursos.


Reconocimientos [<>] [\/] [/\]

      Esta investigación se realizó dentro del proyecto de investigación 326-86-053 "Conversión automática de programas después de reestructurar una base de datos", inscrito ante la Vicerrectoría de Investigación de la Universidad de Costa Rica. La Escuela de Ciencias de la Computación e Informática también ha aportado fondos para este trabajo.



Notas de pie de página [<>] [\/] [/\]

[1] En realidad Sócrates el Griego nunca dijo eso, pero si en su tiempo hubiera habido computadores, no dudo que habría dicho algo parecido.
[2] En realidad en programa sin documentación simplemente no es programa.


Bibliografía [<>] [\/] [/\]

[BM-85]
         
Berry, R.E. & Meekings, B.A.E: A Style Analysis of C Programs, Communications of the ACM, Vol.28 No.1, pp [80-88], Enero 1985.
[BI-88] Borland International: Turbo Pascal Reference Manual version 5.5, Borland International, California (U.S.A.), 1988.
[Bou-91] Boundy, David: A taxonomy of programmers, ACM SigSoft, Vol.16 No.4, pp [23-30], Octubre 1991.
[DiM-89] Di Mare, Adolfo: Abstracción de Datos en Pascal, Reporte técnico ECCI-01-89, ECCI-UCR, 1989.
[JJ-87] Jacky, Jonathan P. & Kalet, Ira J.: An Object-Oriented Programming Discipline for Standard Pascal, Communications of the ACM, Vol.30 No.9, pp [772-776], Setiembre 1987.
[KP-78] Kernighan, Brian W. & Plauger, P. J.: The Elements of Programming Style, second edition, McGraw-Hill Book Company, New York, 1978.
[LEc-87] L'Ecuyer, Pierre: Formal Formatting Rules for Pascal Programs, The Journal of Systems and Software, Vol.7, pg 311-322, 1987.
[LG-86] Liskov, Barbara & Guttag, John: Abstraction and Specification in Program Development, McGraw-Hill, 1986.
[Ret-91] Rettig, Marc: Nobody Reads Documentation, Communications of the ACM, Vol.34 No.7, pp [19-24], Julio 1991.
[Sie-2002] Sieler, Stan: How To Code: Pascal , 2000-05-03.
      http://www.allegro.com/papers/htpp.html
[Str-86a] Stroustrup, Bjarne: The C++ Programming Language, Addison-Wesley, 1986.
[Str-86b] Stroustrup, Bjarne: An Overview of C++, Sigplan Notices, Octubre 1986.
[Sun-99] Hommel, Scott et al: Code Conventions for the Java Programming Laguage, Sun Microsystems, Inc., Abril 20, 1999.
      http://java.sun.com/docs/codeconv/CodeConventions.pdf
[Wir-82] Wirth, Niklaus: Programming in Modula-2, Second Edition, R.R. Donnelley & Sons, Harrisonburg, Virginia U.S.A., 1982.
[Wir-88] Wirth, Niklaus: From Modula-2 to Oberon, Software Practice and Experience, Vol.23 No.7, pp 661-670, Julio 1988.


Indice [<>] [\/] [/\]

[-] Resumen
[1] Introducción [15] Indentación del IF
[2] Documentación de sistemas [16] Indentación del FOR
[3] Documentación de programas       [17] Indentación del WHILE
[4] Documentación externa [18] Indentación del REPEAT
[5] Documentación interna [19] Indentación del CASE
[6] Identificadores [20] Selecciones Múltiples ELSE IF
[7] Constantes [21] Indentación del WITH
[8] Palabras Reservadas [22] Procedimientos
[9] Comentarios [23] Unidades
[10] Instrucciones [24] ADTs
[11] Pragmas [25] Uso de Variables Globales
[12] Declaraciones [26] El programa principal
[13] Expresiones [27] Ejemplo
[14] Indentación [28] Conclusiones


Notas de pie de página [-] Agradecimientos
Bibliografía [-] Reconocimientos
Indice
Acerca del autor
Acerca de este documento
[/\] Principio [<>] Indice [\/] Final


Acerca del autor [<>] [\/] [/\]

Adolfo Di Mare: Investigador costarricense en la Escuela de Ciencias de la Computación e Informática [ECCI] de la Universidad de Costa Rica [UCR], en donde ostenta el rango de Profesor Catedrático. Trabaja en las tecnologías de Programación e Internet. Es Maestro Tutor del Stvdivm Generale de la Universidad Autónoma de Centro América [UACA], en donde ostenta el rango de Catedrático y funge como Consiliario Académico. Obtuvo la Licenciatura en la Universidad de Costa Rica y la Maestría en Ciencias en la Universidad de California, Los Angeles [UCLA].

[mailto] Adolfo Di Mare <adolfo@di-mare.com>


Acerca de este documento [<>] [\/] [/\]

Referencia: Di Mare, Adolfo: Convenciones de Programación para Pascal, Reporte Técnico ECCI-01-88, Proyecto 326-86-053, Escuela de Ciencias de la Computación e Informática, Universidad de Costa Rica, 1988.
Internet: http://www.di-mare.com/adolfo/p/convpas.htm
Autor: Adolfo Di Mare <adolfo@di-mare.com>
Contacto: Apdo 4249-1000, San José Costa Rica
Tel: (506) 207-4020       Fax: (506) 438-0139
Revisión: ECCI-UCR, Marzo 1998
Visitantes:


Copyright © 1997 Adolfo Di Mare
Derechos de autor reservados © 1997 Adolfo Di Mare <adolfo@di-mare.com>
[home] [<>] [/\]