O que faz de nós bons programadores?

Por Rita Peres *



Este artigo nasceu de uma recente discussão de que fui espectadora, e creio que a discussão se iniciou devido ao editorial do número anterior da revista Programar. E tomando a liberdade de citar António Santos: "Nos dias de hoje, mais do que nunca existe tendência a escrever "spaghetti-code" sem grande qualidade, consumidor de enormes recursos de hardware, muitas vezes "usando e abusando" de código gerado automaticamente pelas IDEs, etc…"

A discussão, como o leitor poderá facilmente perceber, foi acerca da arte de bem programar e até que ponto somos bons e maus programadores. No fundo acho que todos os intervenientes da dita discussão estavam correctos. E foi neste contexto, como leitora assídua que sou da Programar, que resolvi dedicar-me a esta análise. Na tentativa de perceber, tal como o leitor, em que ponto do movimento estamos nós, em que ponto da evolução nos encontramos.


Após uma pequena pesquisa que qualquer um de nós poderia fazer, deparei-me com mais de meia centena de linguagens de programação, que podem ser encontradas de A a Z, todas diferentes entre si mas iguais no seu principal objectivo: o de ajudar o mundo a evoluir. Porque no fundo é isso que nós, programadores, tentamos fazer: construímos programas não só pela necessidade de trabalhar em troca de um salário, mas pomos um pouco de nós em cada programa que fazemos, tentando sempre ajudar o nosso utilizador final a atingir um objectivo. A nossa função é simplificar esse processo e fazemos isto em cada módulo que desenvolvemos. Se nos recordarmos da essência do que aprendemos, todos os programadores sabem que devem "dividir para conquistar".

[caption]Nome da imagem[/caption]

Apesar de ser um mote das linguagens de programação orientadas a objectos (como Java), todos nós aprendemos que para criarmos bons programas, devemos dividi-los em partes lógicas. E vamos resolvendo os problemas um a um, módulo a módulo, de forma a conquistarmos um todo, o objectivo que queremos, a simplicidade de uma acção.



A programação tem sempre uma vertente modular. Recordo as palavras de um professor meu, que nos ensinou que "um bom programador, é um bom preguiçoso", porque tenta, através de funções, optimizar o seu código para que seja genérico e possa ser facilmente usado em várias etapas de um projecto sem precisar de alterações. Usamos apontadores e abusamos dos objectos e das estruturas porque sabemos que nos facilitam a vida.



Mas hoje em dia os computadores a que temos acesso são cada vez mais potentes, mais rápidos e, às vezes, o típico código "esparguete" ou o "código pastilha" podem não demonstrar ao utilizador comum que estão a alocar recursos a mais. Se o programa fizer o que o utilizador espera, o utilizador nunca irá pensar no que está por trás.



Mas além da rapidez, a evolução trouxe-nos também a autonomia. E hoje em dia precisamos de programar tendo em conta o dispositivo no qual a nossa aplicação será mais utilizada (o processamento e a memória de um tablet ou de um smartphone ainda não se assemelham à memória e ao processamento de um computador convencional). Nestas circunstâncias temos sempre que nos lembrar de que não podemos alocar mais recursos do que o realmente necessário, senão corremos o risco de ninguém querer usar a nossa aplicação.



Mas voltando ao ponto fulcral do nosso artigo… com um pouco de boa vontade poderíamos definir a nossa informática como um ciclo.



Podemos falar da Máquina de Turing, com a qual Alan Turing deu início a uma nova descoberta criando o primeiro "computador" teórico, em que a máquina fazia qualquer cálculo que lhe fosse pedido. E passar pela Tese de Church, o cálculo Lambda, sem esquecer as funções recursivas de Kleene. Se juntarmos tudo, estamos perante o nascimento da noção de algoritmo que hoje conhecemos. Com as funções recursivas de Kleene é possível programar o cálculo Lambda e este, por sua vez, permite-nos programar e construir a máquina de Turing correspondente a qualquer problema que queiramos resolver.



No entanto, quando programamos, não é com modelos teóricos com que nos preocupamos mas sim com a causa/efeito que queremos. E procuramos ser especialistas numa determinada "família" de linguagens de programação. Escolhemos entre os diversos paradigmas (estrutural, imperativo, entre outros), passamos para a programação orientada a objectos com Java, PHP, aprofundamos o conhecimento no paradigma funcional com Ocaml, Haskell, damos uns toques nas linguagens de markup e de hipertexto como o HTML e CSS, e olhamos com curiosidade para o paradigma lógico com o Prolog, por exemplo.



E com um pouco de pesquisa descobrimos um paradigma novo, o esotérico, que deu origem às esolangs. Sendo estas últimas linguagens de programação projectadas para testar os limites dos projectos de linguagem de computadores, como uma aplicação de uma teoria embora não seja possível utilizá-las na prática. Algumas destas linguagens procuram ser "Turing completas" ou seja, equipotentes à máquina de Turing.



Mas esta forma de caminharmos é apenas um exemplo, e tal como eu, o leitor pode escolher sempre o próximo passo a dar, pois são diversos os paradigmas que podemos escolher. E nem anos de estudo ou de prática fazem com que nos possamos auto-proclamar especialistas. A informática é um mundo em constante evolução, e temos os últimos anos para nos provar isso. Deixámos de programar com cartões furados… e passámos a programar com linhas de código. Com GUIs e IDEs, e hoje em dia já temos quem programe visualmente, arrastando componentes para desenhar a sua aplicação (como acontece no NetBeans, por exemplo).



O facto de tantas pessoas contribuírem para esta nossa revista, a Programar, mostra que em Portugal são muitos os que se preocupam em evoluir, em descobrir, e atingir novos conhecimentos. Não esquecemos o passado para projectarmos o futuro.



Um informático ou programador hoje em dia tem que saber de design, tem que ser um bom gestor, tem que ter muitas vertentes. Porque não vamos desenvolver um programa que não seja agradável à vista do nosso utilizador alvo, não vamos colocar todas as opções umas em cima das outras, temos que gerir o espaço que temos disponível. Desenvolver uma ideia, um projecto e só depois implementá-lo.



Na minha opinião, um bom programador não depende apenas da linguagem que utiliza, uma vez que todos nós temos opiniões e gostos diferentes. Apesar de que programar numa linguagem de programação bem nossa conhecida é praticamente "meio caminho" andado. Um ponto importante na nossa formação é a forma como desenvolvemos um algoritmo, os passos que damos para atingir um objectivo, se programamos ou não de forma recursiva. Isso é talvez o ponto que acho que distingue os bons e os maus programadores.



E isto, caro leitor, traz-nos de volta a um novo ponto do ciclo inicial. A linguagem de programação. E o que é uma linguagem de programação?



Não adianta escrever sobre um assunto se evitamos dar uma definição do mesmo. "Uma linguagem de programação é um método padronizado para comunicar instruções para um computador. Permite que um programador especifique precisamente sobre quais dados um computador vai actuar, como estes dados serão armazenados ou transmitidos e quais acções devem ser tomadas sob várias circunstâncias."



Ora, se o leitor considerar uma linguagem de programação um alfabeto, sendo este um conjunto constituído por letras, que originam palavras que por sua vez originam frases, que servem para comunicar e trocar informação podemos afirmar que um programa é um conjunto de regras sintácticas e semânticas usadas para definir uma acção pretendida.



O que nos leva a percorrer mais um espaço no ciclo. Sendo o nosso programa uma sequência de caracteres precisa de um compilador que leia e interprete os nossos comandos.
Para ser um bom informático, um bom programador, há que conhecer a linguagem de programação utilizada. E qual é a melhor forma de conhecer essa linguagem? Conhecer o seu compilador. E a melhor forma de o conhecer é escrevê-lo.



A construção dum compilador envolve a utilização de vários métodos e ferramentas de análise léxica e sintáctica. A última fase da compilação é a geração de código que é realizada em várias etapas que correspondem a tradução para várias linguagens intermédias antes de se concluir pela produção de código executável. E dando a conhecer um pouco de forma rápida as principais fases da compilação:



Análise léxica é a primeira fase do compilador. A função do analisador léxico, é ler o código fonte, caractere a caractere, separar e identificar os elementos do programa fonte, normalmente chamados símbolos léxicos ou tokens. O objectivo dos geradores automáticos é limitar o esforço de programação de um analisador léxico especificando-se apenas os tokens a ser reconhecidos. Ou seja, o objectivo da análise léxica é reconhecer palavras e neste contexto, um alfabeto são os caracteres do ficheiro fonte, uma linguagem é o conjunto de unidades léxicas e o output é uma sequência de unidades léxicas reconhecidas.



A análise sintáctica, também conhecida como análise gramatical é o processo de se determinar se uma lista de unidades léxicas pode ser gerada, ou não por uma determinada gramática. O analisador sintáctico é responsável por verificar se os símbolos contidos no programa fonte formam um programa válido ou não. Os objectivos desta fase são:



« Reconhecer a estrutura do programa

« Ver se o programa respeita a gramática prevista

« Construir a árvore de sintaxe abstracta

« Construir a tabela de símbolos

Nesta fase, o alfabeto são as unidades léxicas, a linguagem é o conjunto das sequências das unidades léxicas que respeitam a gramática, o input a sequência de tokens e o output é a árvore de sintaxe abstracta por preencher com a tabela de símbolos correspondente.



A análise semântica, apesar de ocorrer em último lugar, não é menos importante do que as duas fases anteriores. Isto porque as análises léxica e sintáctica não estão preocupadas com o significado ou semântica dos programas que processam. O papel do analisador semântico é prover métodos pelos quais as estruturas construídas pelo analisador sintáctico possam ser avaliadas ou executadas. Assim, esta análise não se reduz a um problema de reconhecimento da linguagem, trata-se é de reconhecer a validade das frases construídas. É nesta fase que se "decora/preenche" a árvore de sintaxe abstracta, que nos permite ter uma representação estruturada e independente da sintaxe dos programas analisados.



Um bom programador, se conhecer o seu compilador alvo, está melhor preparado para evitar erros de código, de forma a que possa evoluir mais facilmente na construção do seu programa. Quando "ajudamos" o compilador a fazer o seu trabalho, podemos ter a certeza que o código gerado será sempre o mais optimizado.



E por isto mesmo, a não ser que o leitor enverede pela engenharia da compilação, todos nós temos o nosso trabalho facilitado quando implementamos um programa. Porque temos a certeza que já houve um outro programador que pensou numa forma de reconhecer os erros e de nos ajudar, dizendo-nos qual o erro de compilação que estamos a cometer, ou se temos código que não estamos a utilizar, os chamados "dead blocks".



Contudo caso queiramos mais e queiramos uma linguagem mais específica, devemos sempre recordar que a qualquer momento podemos ser nós a escrever a nossa própria linguagem de programação e para isso basta que aceitemos o desafio de escrever o nosso próprio compilador. E escrever um compilador é olhar para trás e ver alguns dos passos que Turing, Church e Kleene trilharam há muitos anos atrás.



Apesar de o terem feito de forma diferente, qualquer problema dos nossos dias poderia ser resolvido com o modelo de computação destes génios de outrora. E assim, no fim deste artigo, voltamos ao início, "encerrando o ciclo" a que nos propusemos.



Em suma, uma das principais metas das linguagens de programação é permitir que programadores tenham uma maior produtividade, programando acções entendidas mais facilmente do que quando comparado com a linguagem que um computador entende nativamente (código de máquina).
E no fim, todos os compiladores, todas as linguagens rumam para o mesmo objectivo… À nossa vontade, enquanto programadores, de simplificar a vida do utilizador. E de certa forma de ajudarmos a melhorar o mundo. Porque melhorar é simplificar, e simplificar é tornar tudo mais claro. E é termos vontade sempre de melhorar, todos os dias, independentemente de usarmos C, Java ou qualquer outra linguagem.



Porque enquanto bons programadores, voltaremos ao início… o "Hello World"… o que apesar de ser num sentido figurado, este pequeno programa que todos nós conhecemos, é a base de todos nós. Porque sendo uma parte de nós… cada programa que escrevemos… é uma nova forma de dizermos… "Olá Mundo".



* Estudante de Engenharia Informática da Universidade da Beira Interior e membro da comunidade Portugal a Programar (P@P). O artigo está também disponível na edição 37 da revista Programar e é publicado no TeK no âmbito de uma parceria com aquela comunidade.