O exercício de escrever um programa só por si não tem nexo algum. O propósito de escrever um programa é o de resolver um problema. No entanto é uma tendência de muitas pessoas, pensar num problema, e começar a escrever código de seguida. Para resolver problemas com sucesso é necessário pensar um pouco mais profundamente, mesmo para os problemas mais simples.
Uma citação do livro (“Programming Pearls”):
“A Problem Solver’s Perspective: Good programmers are a little bit lazy: they sit back and wait for an insight rather than rushing forward with their first idea. That must, of course, be balanced with the initiative to code at the proper time. The real skil, though, is knowing the proper time. That judgment comes only with the experience of solving problems and reflecting on their solutions.”
Portanto, a mensagem deste post serve apenas para transmitir a ideia de que é preciso construir uma solução antes de construir um programa.
Tomemos como exemplo o exercício do corrector ortográfico pedido como exercício das aulas.
Pretende-se construir uma aplicação para aceitar frases da entrada padrão (standard input) e imprimir na saída padrão (standard output) as palavras que não sejam encontradas num dicionário dado. Podemos esquematizar a solução pretendida da seguinte maneira:
Carregar todas as palavras do dicionário de um ficheiro para memória
Repetidamente
Aceitar frase do input
Separar a frase em palavras
Para todas as palavras dessa frase
Verificar se a palavra existe.
Se a palavra não existe imprime-a
Podemos considerar que temos aqui uma descrição da solução do problema em grandes blocos. Temos agora que descrever a solução em mais detalhe e por fim escrevê-la utilizando uma linguagem de programação. O paradigma de programação utilizado nestas aulas é o paradigma orientado por objectos, e a linguagem, o Java. Devemos portanto desenhar a nossa solução segundo essa perspectiva, dividindo os dados e o processamento em objectos.
Ora, olhando para este problema temos claramente dois intervenientes: um dicionário, um objecto que guarda todas as palavras aceites e que verifica se uma dada palavra consta ou não desse conjunto; e temos outro interveniente, um verificador de frases, que dada uma frase a decompõe em palavras e verifica a sua existência no dicionário. Existe para além disso um objecto que é a parte visível da aplicação que estamos a construir, que implementa a interacção com o utilizador.
Criamos então uma classe Dicionário, cujos dados são as palavras aceites. Essas palavras devem ser carregadas de um ficheiro dado e guardadas em memória. Para além do carregamento das palavras, a classe dicionário tem apenas mais uma operação associada. A verificação da validade de uma palavra.
Quanto aos dados do dicionário, a estrutura de dados mais óbvia para guardar um conjunto fixo de palavras é um vector de palavras. Sendo um conjunto de palavras de dimensão considerável, a maneira mais correcta de pesquisar uma palavra num vector é a busca dicotómica (muito mais eficiente que uma busca linear). Para se poder efectuar uma busca dicotómica é necessário que o vector esteja ordenado. Note-se que o ficheiro dado já está ordenado (no entanto, mistura palavras com maiúsculas com palavras com minúsculas). Para resolver este “pequeno” problema, pode-se ordenar novamente com a ordem “normal” das palavras, ou alternativamente pesquisar ignorando o “case” das palavras ou guardando-as já sob a forma só com minúsculas (ou só maiúsculas). Utilizei a palavra “normal” acima para referir a ordem implementada pela classe string, pelo método compareTo onde as letras maiúsculas vêm todas antes das letras minúsculas.
Outra classe, que pode ser chamada de Corrector, guarda uma referência para um dicionário (fornecido na criação do mesmo), e aceita frases para serem verificadas. Essa operação tem como resultado um conjunto de palavras erradas (um vector).
Assim, a descrição acima (para a solução do problema) poderia ser traduzida da seguinte maneira no código da classe principal:
classe CorrectorApp {
public static final String DICT_FILENAME = "dictionary.txt";
public static void main(String args[]) {
Dictionary dict = new Dictionary(DICT_FILENAME);
Speller speller = new Speller(dict);
Scanner s = new Scanner(System.in);
while( s.hasNextLine() ) {
String[] wrongWords = speller.verify(s.nextLine());
for( String s: wrongWords) System.out.println(s);
}
}
}
Estão aqui evidentes partes do problema que não estão ainda resolvidas. A leitura das palavras do ficheiro, a pesquisa, a partição da frase em palavras, a construção do vector de palavras erradas, etc. O que aqui está presente é tão só a estruturação da solução, a identificação de problemas mais pequenos que podem ser resolvidos usando o mesmo método de decomposição de problemas.
Espero ter acendido algumas luzes mesmo que pequenas.