Como programar

É comum que os professores de IAlg sejam procurados por alunos com um dúvida bem genérica: Como programar? Essa dúvida é tão fundamental que é até difícil explicar o que é a dificuldade encontrada pelo aluno. Geralmente essa pergunta reflete a dificuldade geral do aluno na disciplina e é fruto de diversos fatores culturais e sociais. Vou tentar ajudar com esse texto.

Em primeiro lugar, é importante destacar que essa dúvida reflete um problema profundo de entendimento do que significa estudar, aprender, resolver problemas, fazer trabalhos, etc. Alguns alunos chegam na universidade pensando que aula é uma atividade em que alguém te ensina várias respostas, uma para cada pergunta de um conjunto, de forma que mais a frente você receberá uma dessas perguntas como avaliação para verificarem se você sabe a resposta. Isso é absurdo, mas infelizmente é comum.

Em IAlg, você aprende conceitos e técnicas importantes para programação, depois recebe problemas para resolver e será avaliado conforme sua solução demonstrar que você é capaz de usar esses conceitos e técnicas de maneira adequada enquanto resolve problemas. Não existe "a resposta" para o problema, existem inúmeras respostas, algumas melhores, outras piores, algumas não são propriamente soluções para o problema, etc.

Depois de entender que não existe "a resposta", ainda permanece a pergunta: como eu consigo produzir uma resposta boa? Nesse ponto é importante destacar que as respostas não caem do céu de forma inexplicável. Pode até parecer isso quando você vê o professor resolvendo um problema, mas isso é porque ele já resolveu tantos outros problemas que o processo de construção da resposta é super rápido. Mas a construção está lá. Quando o professor aprendeu a programar ele não conseguia construir uma resposta tão rápido assim. Você pode notar esse processo de construção quando uma solução de um professor não funciona perfeitamente. Quem está preso a uma cultura de "qual a resposta?" logo fica com raiva, pensando que o professor é incompetente, ou que perdeu tempo escrevendo uma solução passada pelo professor que não é bem uma solução. A questão não é que a solução está errada, a questão é que o professor consegue corrigir a solução logo que identifica o erro. Aí está a evidência da construção.

A produção de uma solução é um processo incremental, ou seja, a solução vai sendo construída pouco a pouco, numa repetição de projetar, testar, corrigir até que o desenvolvedor considere que já está na hora de seguir em frente e resolver o próximo problema. Eu costumo dizer que todo programa está errado, mas alguns estão tão pouco errados que merecem ganhar nota 100.

Tendo em mente esse contexto mais amplo da dificuldade que muitos encontram na programação, uma sugestão de passos para produzir soluções é:

  1. Projetar;
  2. escrever (implementar);
  3. testar e
  4. voltar ao início (dessa vez, detalhes diferentes do problema estão ocupando seus pensamentos).

Projetar (planejar) a solução

Essa é provavelmente a parte mais difícil de se ensinar e isso tem muito a ver com a cultura de saber "a resposta". Decorar resposta não te ajuda nessa parte. Com frequência vemos alunos fazendo grandes coleções de respostas e esses alunos não costumam ir bem nas avaliações.

Resolver problemas é inerente da natureza humana. Antes de aprender a andar, deslocar-se para outro lugar era um problema importante na sua vida. Alguns resolviam o problema rolando, outros se arrastavam usando os braços, outros se arrastavam usando as pernas, outros ainda levantavam a mão para ganhar colo e transporte da mãe. As soluções para esse problema eram inúmeras e você deve ter experimentado com mais de uma, repetindo estratégias de sucesso no passado e abandonando aquelas com mais desvantagens. Eventualmente, essas tentativas fizeram você adquirir habilidades de controle de movimento superiores para resolver esse problema de deslocamento de uma maneira mais vantajosa: andar. Observe que ninguém nasceu sabendo andar; quem usou mais a solução da mãe demorou mais para aprender a andar e que andar não é uma solução perfeita sem qualquer desvantagem.

Os professores não podem te ensinar a resolver problemas, além disso, resolver problemas com você olhando ajuda muito pouco no desenvolvimento da sua habilidade de resolver problemas. O que os professores podem fazer é explicar vantagens e desvantagens de rolar, se arrastar ou pedir color e depois mandar você se deslocar muitas vezes, até adquirir habilidade suficiente para andar.

Para projetar a solução de um problema, resolva o problema sem pensar em como os computadores funcionam, sem pensar em comando de uma linguagem de programação. Imagine que você tem que resolver o problema sem qualquer interação com um computador. Você consegue resolver assim? Em caso negativo, é preciso pensar mais, raciocinar.

Resolver o problema significa no nosso contexto que você tem que ser capaz de resolver todas as instâncias do problema. Por exemplo: saber calcular a área de um retângulo significa ser capaz de calcular a área de qualquer retângulo, saber calcular a área de retângulo 1 x 1 sem saber calcular a área de um retângulo 0.12 x 3.14 significa que na verdade você não está sabendo calcular a área de um retângulo. Mas se você sabe, está na hora de produzir um algoritmo.

A parte mais difícil de produzir um algoritmo é formalizar uma sequência de atividades que vai permitir resolver o problema mecanicamente, ou seja, sem ter que pensar sobre o que está sendo feito. Fazer isso exige treino. É uma habilidade que você adquire fazendo várias vezes. Para ajudar no desenvolvimento dessa habilidade os professores passam muitos exercícios, aumentando a dificuldade com o tempo. Josh Darnit mostra a dificuldade de se produzir sequências precisas de instruções que resolvem um problema nessa brincadeira que ele postou no YouTube (se você conhece um vídeo semelhante em português, por favor me informe para eu substituir aqui).

Quando se está aprendendo é recomendável escrever essa solução algorítmica em papel ou talvez no computador, na forma de descrição narrativa, pseudocódigo ou fluxograma. Eu pessoalmente sugiro usar a descrição narrativa. Outros professores da UFLA preferem pseudocódigo. É comum que alguém com muita prática faça isso mentalmente em um segundo, dando a impressão que pular essa parte é mais eficiente. É comum que os alunos se recusem a fazer essa parte com a justificativa de que não querem (ou não podem) gastar tempo fazendo essa etapa. Isso é um erro. Fazer tudo com calma separadamente, de pouco em pouco, é justamente para evitar a dificuldade de se tentar resolver tudo ao mesmo tempo. Você não pode ficar pulando as etapas e depois reclamar que está difícil.

A sequência de passos vista acima (passos que devo seguir para produzir um programa) é um exemplo dessa etapa de planejamento. Saber fazer isso não é de forma alguma uma perda de tempo.

Escrever o programa

Depois de produzir uma solução escrita, está na hora de produzir código fonte. Nessa etapa é que você acrescenta os detalhes relativos ao computador e à linguagem de programação. Por exemplo: na etapa anterior você resolveu que precisa saber a idade de alguém. Agora está na hora de pensar: "Já que esta linguagem de programação não tem o tipo idade, qual tipo eu vou usar para representar uma idade?". Ou ainda: na etapa anterior você resolveu que precisa repetir algumas coisas até acaberem os itens sendo processados e agora você precisa pensar em que controle você pode criar para saber se os itens a processar já terminaram.

A primeira implementação provavelmente estará errada. Não é normal produzir uma implementação correta logo na primeira tentativa. É provável que seu programa nem sequer esteja atendendo as regras de sintaxe da linguagem, quando mais resolvendo o problema. Lembre-se que estamos num processo de melhorar pouco a pouco "eternamente". Se o seu programa não compila, preste atenção às mensagens do compilador, reveja os trecho que ele diz estarem errados. Tente encontrar e corrigir os erros. Não fique brincando de adivinhar: "não sei qual o erro, mas vou tentar mudar seis por meia dúzia para ver se por algum motivo o compilador para de indicar erro".

Escrever o programa é uma etapa relativamente pouco importante, mas que ganha bastante atenção de quem não está acompanhando a filosofia da disciplina. Não quero dizer como isso que não é preciso escrever o programa corretamente e de acordo com as regras rígidas da linguagem de programação. Quero dizer que é menos importante conhecer regras de sintaxe e nomes de comandos, ou ainda conhecer uma grande variedade de comandos disponível. É importante que você saiba se virar conhecendo apenas o comandos fundamentais, que apareceram anteriormente nas aulas. É importante ser capaz de entender as mensagens de erro do compilador, identificando e corrigindo os erros como se você soubesse a sintaxe dos elementos usados. É comum, mesmo em avaliações presenciais, que os professores respondam suas dúvidas sobre sintaxe.

Testar o programa

Depois que seu programa compila, está na hora de executá-lo e ver se ele encontra a solução correta para todas as instâncias. Nessa etapa, vários perguntam: "como vou saber que a solução que o programa deu está correta?". Oras, a etapa de planejamento requer que você saiba resolver o problema. Se você não é capaz de dizer se a resposta está correta, significa que você não sabe resolver o problema, então você deve voltar ao planejamento.

Nesta etapa também é importante pensar sobre quais testes fazer. Por exemplo: se o problema é calcular a área de um retângulo, dadas a altura e a largura de um retângulo, qual altura e qual largura eu devo usar no teste? É comum que os alunos não queiram pensar sobre isso e simplesmente usem os exemplos do problema como instâncias para teste. Isso é um mal hábito. É importante saber escolher casos.

Procure extrair casos do código que você escreveu. Ou seja, se seu programa testa se a idade de alguém é menor que 18, então é bom você testar uma instância em que a idade é menor que 18 e outra instância em que a idade não é menor que 18. Se a idade inferior a 18 leva a outro teste para saber se o peso é maior que um determinado limite, então já são três situações importantes para testar: 1) idade maior igual a 18, 2) idade menor que 18 e peso maior que o limite e 3) idade menor que 18 e peso menor ou igual ao limite. As situações previstas no programa devem ser todas testadas.

Em seguida, é bom procurar situações não previstas. Para isso, eu aconselho pensar nas situações extremas. Encontrar situações extremas sempre leva a pensar no domínio do problema, ou seja, quais são as instâncias para as quais existe e não existe resposta? Se não existe resposta, sou obrigado a produzir alguma ou posso permitir que o programa responda qualquer coisa. Para responder a isso, reveja o enunciado do problema. Seu trabalho não é definir o problema, é produzir a solução para um problema bem definido. Pode acontecer de você encontrar um enunciado mal feito, que não deixa o problema bem definido. Nesse caso, não tente adivinhar. Peça esclarecimentos para o professor. Peça que ele melhore o enunciado quando for necessário.

Por exemplo: o problema é produzir o somatório de um conjunto de valores. O que seriam casos extremos? Seria extremo produzir o somatório de zero valores. Existe resposta nesse caso? Se existe, qual é? Frequentemente essas questões requem que a etapa de planejamento esteja bem resolvida. Pode ser necessário voltar ao planejamento. Em outros casos, podem ser necessários conhecimentos do ensino fundamental e médio, coisas que você não lembra mas pode procurar saber ou perguntar ao professor. Por exemplo: a soma de zero valores é possível e vale zero. Teste seu programa com zero valores, depois teste com um valor só, depois teste com dois valores. É muito improvável que testar com três valores cause a execução de alguma parte diferente do seu programa que não tenha sido testada nos casos anteriores, continuar testando com três ou mais casos seria perda de tempo.

Também é comum que você note que uma determinada instância do problema causa erro mas não consegue identificar o motivo. Você olha seu código e parece estar tudo certo. Nesse caso é importante realizar uma execução passo a passo do programa. Isso é normalmente feito com papel e lápis, desenhando lugares para escrever os valores e seus identificadores, apagando e alterando esses valores conforme você executa mentalmente cada instrução do programa. Essa atividade é conhecida como teste de mesa. Veja exemplos de pessoas realizando testes de mesa. Alunos um pouco mais avançados podem realizar testes de mesa simplesmente adicionando operações de escrita no código, tornando possível olhar o que o programa escreveu ao longo do tempo, para acompanhar como os valores das variáveis foram sendo alterados ao longo das instruções, até encontrar a instrução em que alguma variável fica com um valor inesperado.

A etapa de testes é complicada porque é muito comum descobrir que seu programa está errado, dessa vez sem o compilador ajudando a encontrar os erros. Isso é normal e só melhora com a prática. Nesta etapa, o cuidado com a escolha de bons nomes, uso adequado de comentários e outros fatores de legibilidade compensam o tempo gasto na etapa de escrita. É importante fazer um esforço nesta etapa para não olhar programas de outros pois aqui a tentação do plágio é grande. Em último caso (depois de ter feito um teste de mesa), peça ao professor (e não a um colega) que ajude a encontrar o erro no código.

Esta página é mantida por Bruno Schneider para a disciplina de IAlg