Projeto de Algoritmos

"Programming is best regarded as the process of creating works of literature,
which are meant to be read."
—Donald E. Knuth, Literate Programming

". . . put a comment on each function saying what the function does,
what sorts of arguments it gets,
and what the possible values of arguments mean and are used for  [ . . . ]
Also explain the significance of the return value, if there is one."
—Seção Commenting Your Work em GNU Coding Standards

"Needless to say [that] all by itself,
a program is no more than half a conjecture.
The other half of the conjecture is the functional specification the program is supposed to satisfy.
The programmer's task is to present such complete conjectures as proven theorems."
—Edsger W. Dijkstra, In Pursuit of Simplicity, manuscrito EWD1036

"Computer science is no more about computers than astronomy is about telescopes."
—Edsger W. Dijkstra

"Programs must be written for people to read, and incidentally for machines to execute."
— H. Abelson, G. Sussman, The Structure and Interpretation of Computer Programs

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
— Brian W. Kernighan

Documentação

Há quem pense que documentar um programa é o mesmo que escrever muitos comentários de mistura com o código.  Totalmente errado!  Uma boa documentação não suja o código com comentários. Uma boa documentação limita-se a

explicar o que cada função do programa faz.

Em geral, uma boa documentação não perde tempo tentando explicar como uma função faz o que faz, porque o leitor interessado nesse assunto é capaz de ler o código.

A distinção entre o que e como é a mesma que existe entre a interface (arquivo .h) e a implementação (arquivo .c) de uma biblioteca C.  Eis uma analogia que pode tornar mais clara a diferença. Uma empresa de entregas promete apanhar o seu pacote em São Paulo e entregá-lo no Rio de Janeiro. Isso é o que a empresa faz. Como o serviço será feito — se o transporte será terrestre, aéreo ou marítimo, se haverá ou não uma escala em Belo Horizonte, etc. — é assunto interno da empresa.

Em suma, a documentação de uma função ou algoritmo é um mini-manual que dá instruções precisas e completas sobre o uso correto da função.  A primeira parte desse mini-manual deve dizer o que a função recebe e o que devolve; em C, isso é conhecido como protótipo da função.

Uma documentação correta é até uma questão de honestidade intelectual, pois coloca nas mãos do leitor a real possibilidade de encontrar os erros que o autor tenha porventura cometido ao escrever o código.

Veja também o verbete Software documentation na Wikipedia.

Exemplo

Em uma das páginas destas notas escrevemos uma função que calcula o valor de um elemento máximo de um vetor. Vamos repetir aqui o código daquela função juntamente com uma documentação perfeita:

/* A função abaixo recebe um número n >= 1 e um vetor v
 * e devolve o valor de um elemento máximo de v[0..n-1]. 
 ******************************************************/

int
max (int n, int v[])
{ 
   int j, x = v[0];
   for (j = 1; j < n; j += 1)
      if (x < v[j]) x = v[j];
   return x;
}

Veja como a documentação é simples e exata. A documentação só diz o que a função faz; ela não perde tempo tentando explicar como a função faz o que faz (por exemplo, se a função é recursiva ou iterativa, se percorre o vetor da esquerda para a direita ou vice-versa).  Observe também que não há comentários inúteis (como  "o índice j vai percorrer o vetor"  ou  "x armazena o maior valor encontrado até agora") sujando o corpo da função.

Eis alguns exemplos de má documentação. Dizer apenas que a função

    devolve o valor de um elemento máximo de um vetor

é indecentemente vago. Dizer que a função

    devolve o valor de um elemento máximo do vetor v

é um pouquinho melhor, mais ainda muito vago: o leitor fica sem saber qual o papel do parâmetro n.  Dizer que a função

    devolve o valor de um elemento máximo de um vetor v
    que tem n elementos

é melhor, mas ainda está vago: não se sabe se o vetor é v[0..n-1] ou v[1..n].  Dizer que a função

    devolve o valor de um elemento máximo de v[0..n-1]

já está quase bom, mas sonega a informação de que a função só funciona corretamente se n  1.

Exemplo

Em uma das páginas destas notas escrevemos uma função que decide se um número x é igual a algum elemento de um vetor v.  Vamos repetir aqui o código daquela função juntamente com uma documentação perfeita:

/* Recebe um número x, um vetor v e um índice n >= 0. 
 * Devolve 1 se x está em v[0..n-1] e devolve 0 
 * em caso contrário.
 ***************************************************/

int
verifica (int x, int n, int v[])
{
   int j = 0;
   while (j < n && v[j] != x)  
      j += 1;
   if (j < n) return 1;
   else return 0; 
}

Veja como a documentação diz, de maneira exata e completa, o que a função faz. Veja como a documentação não perde tempo tentando explicar como a função faz o serviço.

Para contrastar, eis alguns exemplos de má documentação.  Dizer

    decide se x está em v[0..n-1] 

é um tanto vago; dizer

    decide se x está no vetor v 

é muito vago; dizer

    decide se um número está em um vetor 

é absurdamente vago.

Mais um exemplo

Eis uma função acompanhada de documentação perfeita:

/* Recebe x, v e n >= 0 e devolve j 
 * tal que 0 <= j < n  e  v[j] == x.
 * Se tal j não existe então devolve n.
 *************************************/

int
onde (int x, int v[], int n)
{
   int j = 0;
   while (j < n && v[j] != x)  
      j += 1;
   return j;
} 

Exercício

  1. Um segmento horizontal de um vetor a é um subvetor a[i..k] cujos elementos têm todos um mesmo valor. Considere a função
    int
    compmax (int a[], int n)
    {
       int i, k, max = 1;
       i = 0;
       for (k = 1; k <= n; k++)
           if (k == n || a[k] != a[i]) {
              if (max < k-i) max = k-i;
              i = k;
           }
       return max;
    }
    

    A documentação abaixo está correta?

    /* a função recebe um vetor a[0..n-1] e devolve
     * o comprimento de um segmento horizontal máximo do vetor
     *********************************************************/
    

    A seguinte documentação está correta?

    /* a função recebe um vetor crescente a[0..n-1] com n >= 1
     * e devolve o comprimento de um segmento horizontal máximo
     * do vetor
     *********************************************************/
    

Dica

Existem várias ferramentas sofisticadas que permitem uma excelente integração de código com documentação.  Veja minha página sobre literate programming e CWEB.  Eis alguns exemplos de programas escritos em CWEB: mdp, isort.  [Software para ler arquivos .pdf]

Invariantes

O corpo de muitas funções consiste em um processo iterativo (um for ou um while). Nesses casos, depois de dizer o que a função faz, você pode enriquecer a documentação dizendo quais as invariantes do processo iterativo. Uma invariante é uma relação entre os valores das variáveis que

vale no início de cada iteração e não se altera de uma iteração para outra.

Essas relações invariantes explicam o funcionamento do processo iterativo e permitem provar, por indução, que ele tem o efeito desejado.

Eis um exemplo. A função max recebe um número n  1 e um vetor v e devolve o valor de um elemento máximo de v[0..n-1]:

int
max (int n, int v[])
{ 
   int j, x = v[0];
   for (j = 1; j < n; j++)
      /* neste ponto, x é um elemento máximo de v[0..j-1] */
      if (x < v[j]) x = v[j];
   return x;
}

Outro exemplo: a função compmax recebe um vetor a[0..n-1] e devolve o comprimento de um segmento horizontal (veja exercício acima) máximo do vetor:

int
compmax (int a[], int n)
{
   int i, k, max = 1;
   i = 0;
   for (k = 1; k <= n; k += 1)
       /* neste ponto, max é o comprimento de
        * um segmento horizontal máximo de a[0..i-1]
        * e a[i..k-1] é um segmento horizontal
        *******************************************/
       if (k == n || a[k] != a[i]) {
          if (max < k-i) max = k-i;
          i = k;
       }
   return max;
}

Vários exemplos de prova de correção de algoritmos baseada em invariantes aparecem, por exemplo, nas páginas dedicadas à busca binária, à ordenação, ao mergesort, ao heapsort, ao quicksort.

(O conceito de invariante é análogo ao que existe na Física.  Albert Einstein era particulamente habilidoso em descobrir invariantes — como a velocidade da luz, por exemplo.)

 


URL of this site: www.ime.usp.br/~pf/algoritmos/
1998 | Last modified: Mon Jan 8 08:49:13 BRST 2007
Paulo Feofiloff
IME-USP

Valid HTML 4.0 Transitional    Valid CSS!