TÉCNICAS DE PROGRAMAÇÃO (Adaptado do texto do prof. Adair Santa Catarina) ALGORITMOS COM QUALIDADE MÁXIMAS DE PROGRAMAÇÃO 1) Algoritmos devem ser feitos para serem lidos por seres humanos: Tenha em mente que seus algoritmos deverão ser lidos por outras pessoas (e por você mesmo) de tal forma que possam ser corrigidos, receber manutenção e ser modificados. 2) Escreva os comentários no momento em que estiver escrevendo o algoritmo: Um algoritmo não documentado significa amadorismo. Como o objetivo de se escrever comentários é facilitar o entendimento do algoritmo, eles dever ser tão bem concebidos quanto o próprio algoritmo. E a melhor maneira de se conseguir isso é escrevê-lo nos momentos de maior intimidade com os detalhes, ou seja, durante a resolução do problema. 3) Os comentários deverão acrescentar alguma coisa; não apenas para frasear os comandos: O conjunto de comandos nos diz o que está sendo feito; os comentários deverão dizer por quê. 4) Use comentários no início (prólogo): Todo algoritmo ou procedimento deverá ter comentários no seu prólogo para explicar o que ele faz e fornecer instruções para seu uso. Alguns destes comentários seriam: a) Uma descrição do que faz o algoritmo; b) Como utilizá-lo; c) Explicação do significado das variáveis mais importantes; d) Estruturas de dados utilizadas; e) Os nomes de quaisquer métodos especiais utilizados, juntamente com referências nas quais mais informações possam ser encontradas; f) Autor; e g) Data de escrita.
5) Utilize espaços em branco para melhorar a legibilidade: Espaços em branco, inclusive linhas em branco, são valiosíssimos para melhorar a aparência de um algoritmo. Por exemplo: a) Deixar uma linha em branco entre as declarações e o corpo do algoritmo; b) Deixar uma linha em branco antes e outra depois de um comentário; c) Separar grupos de comandos que executam funções lógicas distintas por uma ou mais linhas em branco; d) Utilizar brancos para facilitar a leitura e parênteses para indicar precedência de operadores. Repare que ao usar A + B + C fica fácil de entender. E (A + B) / C facilita o entendimento da precedência do execução. 6) Escolha nomes representativos para suas variáveis: Os nomes das variáveis deverão identificar, o melhor possível, os dados que elas representam. Por exemplo, X = Y + Z é muito menos claro que PREÇO = CUSTO + LUCRO. Uma seleção adequada de nomes de variáveis é um dos princípios mais importantes da legibilidade de algoritmos. 7) Um comando por linha é suficiente: A utilização de vários comandos por linha é prejudicial por várias razões, dentre as quais destacam-se: a) O algoritmo fica mais ilegível; b) O algoritmo fica mais difícil de ser depurado. Exemplo: A = 14.2; I = 1; enquanto I < 10 faça X = X + 1; K = I * K; I = I + 1; fim enquanto; O mesmo exemplo com cada comando em uma linha: A = 14.2; I = 1; enquanto I < 10 faça X = X + 1; K = I * K; I = I + 1; fim enquanto; Caso desejássemos incluir um novo comando dentro do enquanto, nota-se que no segundo caso é mais fácil de localizar o ponto exato onde incluí-lo. No primeiro caso, a leitura e identificação deste ponto ficam prejudicadas.
8) Utilize parênteses para aumentar a legibilidade e prevenir-se contra erros: Exemplo: Com poucos parênteses A * B * C/(D * E * F) A * B/C * D/E * F A ** B ** C A/B/C/D X > Y ou Q A + B < C Com parênteses extras (A * B * C) / (D * E * F) (((A * B)/(C) * D)/E) * F (A ** B) ** C ((A/B)/C)/D (X > Y) ou Q (A + B) < C 9) Utilize indentação para mostrar a estrutura lógica do algoritmo: A indentação não deve ser feita de forma caótica, mas segundo certos padrões estabelecidos. Lembre-se: toda vez que for feita uma modificação no algoritmo, os comentários associados devem ser alterados, e não apenas os comandos: Melhor não comentar do que deixar um comentário errado. Exemplos de trechos de algoritmos mal escritos e a análise das regras de programação violadas serão apresentados a seguir. Em alguns casos, o algoritmo equivalente, com qualidade será apresentado. SOMA = 0; {Atribui o valor 0 à variável SOMA} I = 1; {Atribui o valor 1 à variável I} enquanto I < 18 faça {Enquanto I não ultrapassar o valor SOMA = SOMA + I; {18, será atribuído o valor da ex- I = I + 1; {pressão SOMA + I à variável SOMA e o {valor de I será incrementado de 1 fim enquanto escreva (SOMA); {imprime o valor de SOMA} O algoritmo acima fere principalmente a regra n o 3. Ele deveria ser escrito para ser considerado de qualidade da seguinte maneira: início {faz a soma dos números inteiros de 1 a 18} {Autor: EU data: 01/05/2014} inteiro : SOMA, {é o somatório} I; {número inteiro gerado} SOMA = 0; I = 0;
fim. enquanto (I < 18) faça {gera os números inteiros} SOMA = SOMA + I; {calcula o somatório} I = I + 1; fim enquanto; METODOLOGIA PARA DESENVOLVIMENTO DE ALGORITMOS Uma das dificuldades naturais de um iniciante em programação é começar a desenvolver um algoritmo para resolver um dado problema. Os passos seguintes, se seguidos, podem auxiliar nesta tarefa: Passo 1: leia cuidadosamente a especificação do problema até o final, e vá fazendo anotações. Revise a leitura. Pergunte a si mesmo se entendeu o que o algoritmo deve fazer. Enquanto não tiver entendido tudo, ler com atenção redobrada. Se a dúvida persistir, solucioná-la com ajuda do professor ou de colegas. Passo 2: Identificar e analisar todas as entradas citadas na especificação do problema (as leituras); Passo 3: Identificar e analisar todas as saídas exigidas na especificação do problema (as impressões) Passo 4: Verificar se é necessário gerar valores internamente ao algoritmo e levantar as variáveis necessárias e os valores iniciais de cada uma (comentar) Passo 5: Identificar e analisar todas as transformações necessárias para, dadas as entradas e valores gerados internamente, produzir as saídas especificadas (comentar). Passo 6: Testar cada passo do algoritmo, verificando se as transformações intermediárias executadas estão conduzindo aos objetivos desejados. Utilizar, sempre que possível valores de teste que permitam prever os resultados a priori. Passo 7: Fazer uma reavaliação geral, analisando o algoritmo através da integração das partes (rever comentários). À medida que o leitor vá adquirindo maior amadurecimento na confecção de algoritmos, vários dos passos descritos vão sendo feitos automaticamente. Recomenda-se que no início do aprendizado e quando se tratar de problemas mais complexos, os passos sejam seguidos formalmente.
O DESENVOLVIMENTO TOP-DOWN Antes de escrever um programa, de fato sempre é necessário um processo de raciocínio, que leva de uma análise do problema dado, passando por um algoritmo em termos gerais até um algoritmo detalhado, que consiste em uma seqüência de passos simples que podem ser diretamente expressados em termos de comandos numa linguagem de programação. Na programação estruturada as diferentes fases deste processo de concepção de um programa são fixadas explicitamente por escrito, e não só como é uso comum o produto final: o programa na forma em que será submetido à máquina. Obtemos no final não somente um programa (na linguagem que foi escolhida para a codificação), mas também uma documentação de todo o processo de solução do problema. Para as estruturas de dados usamos igualmente representações abstratas de acordo com as necessidades do problema, que vão sendo refinadas até chegar à representação implementável na linguagem de programação. Isso permite desenvolver passo a passo o algoritmo-solução e as estruturas de dados em termos das categorias relevantes ao problema, e não das peculiaridades de uma linguagem de programação. Cada nova fase desse desenvolvimento de cima para baixo é obtida por refinamento da fase anterior, até chegar a um nível de detalhamento que permita implementar o algoritmo diretamente na linguagem de programação. PROGRAMAÇÃO MODULAR Até o momento as estruturas de controle (seqüência, seleção e repetição) de um algoritmo definia-o como um bloco lógico (início e fim). À medida que os problemas a serem solucionados se tornam mais complexos, ou seja, apresentam uma variedade maior de situações diferentes a serem resolvidas, temos, então, uma série de pequenos problemas, cujas respectivas soluções integrarão o conjunto de ações definitivo. Quando tal conjunto de ações é construído, podemos nos deparar literalmente com um amontoado de ações que afetam a legibilidade porque não ficam claras e concisas as pequenas partes lógicas que solucionam cada pequeno problema, o que dificulta a assimilação desta construção por outras pessoa, além de inviabilizar uma perfeita coesão interna do algoritmo. Modularização é a técnica que combate tais circunstâncias e explicita as pequenas soluções. MÓDULOS Módulos são blocos lógicos de um algoritmo que visam aumentar a funcionalidade das partes do conjunto solução.
BLOCOS Um bloco consiste em um conjunto de declarações e comandos delimitados pelas palavras início e fim. DECLARAÇÃO Para modularizar um algoritmo, necessitamos de uma sintaxe para expressar esta nova estrutura compreendida por módulos. Esta sintaxe será vista mais tarde, uma vez que o módulo pode assumir mais de uma forma. Neste momento é importante saber que ao módulo é atribuído um nome (Identificador). De certo modo, precisamos uniformizar determinado conjunto de ações afins que obedeçam a mesma estruturação de um algoritmo com objetivo de representar um bloco lógico em especial. Lembrar que: a) Quando construímos um módulo estamos construindo um algoritmo em instância menor, ou seja, um pequeno conjunto solução praticamente independente. b) Este sub-algoritmo pode inclusive utilizar outros módulos. c) Com o emprego de sub-algoritmos utilizados especificamente para resolver problemas pequenos, aumentou-se o grau de clareza, facilitando a compreensão de cada parte isoladamente, assim como o relacionamento entre elas. MANIPULAÇÃO A ativação de um módulo ocorre quando um determinado ponto do algoritmo contém o identificador que foi usado na definição do módulo, o que é conhecido por chamada ou ativação do módulo que por sua vez representa a execução das ações deste naquele trecho do algoritmo. ESCOPO DE VARIÁVEIS Todas as variáveis utilizadas no algoritmo e que encontram-se declaradas no seu início, tornam-se possíveis de aplicação por qualquer módulo integrante. Estas variáveis são denominadas globais. Em alguns casos uma determinada variável é utilizada apenas por um módulo específico, o que não justifica uma definição global, pois somente se fazem necessários o conhecimento e a utilização dessa variável dentro dos limites desse bloco lógico. Esta situação ocorre quando a variável é declarada internamente ao módulo e é denominada variável local. Ou seja: a) Uma variável declarada dentro de um bloco só é conhecida dentro deste bloco; b) Se uma variável X declarada em um bloco já foi declarada como global e com o mesmo nome declarada num bloco mais interno, a variável válida no bloco mais interno será a declarada neste bloco.
SUBROTINAS Uma subrotina é uma ferramenta de programação que serve basicamente a dois objetivos: Evitar que uma certa seqüência de comandos que ocorra em vários locais de um algoritmo tenha de ser escrita repetidamente nestes locais; Dividir a estrutura de um algoritmo em partes fechadas e logicamente coerentes. Algoritmos contém muitas vezes grupos de comandos iguais e repetidos em várias partes de seu corpo. As subrotinas permitem economia de tempo, de escrita e redução no tamanho de algoritmos. Em alguns casos, os trechos de comandos repetidos são idênticos, porém utilizam operandos diferentes. Nestes casos, a utilização de subrotinas permitem, também, uma única escrita dos comandos que ao serem executados, utilizam os parâmetros ou operandos adequados a cada ocorrência. Bem mais importante que esta primeira utilidade do uso de subrotinas, o seu uso na modularização de algoritmos torna-se uma ferramenta indispensável na elaboração de algoritmos mais complexos e de maior tamanho. A divisão de estruturas de algoritmos em partes lógicas é uma técnica indispensável em programação estruturada. A divisão de algoritmos em subrotinas, além de facilitar a elaboração do algoritmo permite uma melhor documentação e verificação de sua correção. Cada módulo implementado como uma subrotina deve conter sua própria documentação e pode ser verificado independentemente. Adicionalmente a modularização por subrotinas pode facilitar muito o trabalho de equipes de programadores no sentido que um algoritmo pode ser dividido em partes, testado e catalogado separadamente.