Computação, Uma Ciência Exata com Aspirações Humanas e … · 2009-01-09 · la. Não faz...
Transcript of Computação, Uma Ciência Exata com Aspirações Humanas e … · 2009-01-09 · la. Não faz...
COMPUTAÇÃO, UMA CIÊNCIA EXATA COM
ASPIRAÇÕES HUMANAS E SOCIAIS
Aluizio Arcela
I. Antecedentes históricos
Em um dado momento da sua história, eis que o homem aprendeu a contar — um
fenômeno mental manifestado muito mais recentemente do que a descoberta do número.
A rigor, deve-se falar de ‘sentimento do número’ ao invés de ‘descoberta’, posto que
contar é um atributo exclusivamente humano, ao passo que o sentimento do número
ocorre também em outros animais. Contar, que é a forma mais primitiva de computar,
somente se realiza quando se dá nome a uma quantidade ou quando se lhe atribui um
símbolo. Os primeiros vestígios de materialização dessa habilidade de contar
apareceram em escavações arqueológicas feitas em um lugar da Mesopotâmia perto da
Babilônia. Placas de argila contendo algoritmos — que seriam os mais antigos jamais
encontrados, uma vez que datavam do período 2000-3000 a.C. — trouxeram à tona uma
parte do que foi a pré-história da computação. A palavra algoritmo, conforme aqui
empregada, encerra um conceito central em computação que pura e simplesmente
significa uma seqüência de operações voltadas no sentido de se resolver um dado
problema. Isto é, algoritmo é um conjunto finito de instruções, devendo todo algoritmo
apresentar uma entrada, de onde recebe os dados; uma saída, para onde remete o
resultado; instruções bem definidas e sem ambigüidade; e finitude, isto é, um resultado
haverá de ser produzido após um determinado número de passos.
Muito embora a computação seja fixada como ciência somente na segunda metade do
século XX, esta sua noção central — o algoritmo — é um conceito intuitivo e próprio
da natureza humana. Pode-se até dizer que toda tarefa pressupõe um algoritmo, de modo
que, na sua acepção global, a computação é tão antiga quanto a própria humanidade. É
certo que a presença do computador, muitas vezes tida como a expressão máxima da
tecnologia decorrente da ciência da computação, dificulta o discernimento de onde
termina a ciência e onde começa a máquina, mas é importante que não se reduza a
computação ao computador, ainda que ele seja o seu instrumento principal e se constitua
naquilo que legitima a sua condição de ciência da automatização do cálculo. Portanto, a
computação não é a ciência de uma máquina, mas sim a do fenômeno da computação,
fenômeno este que ocorre tanto na vida, seja no plano biológico ou no psíquico, quanto
no cosmo, nas estrelas e em toda a natureza. Em um organismo multicelular, quando
uma célula se divide, por exemplo, ela o faz como um autômato que lê a estrutura da
molécula de DNA interpretando-a como um legítimo programa de computador, sendo
esta uma ocasião em que o fenômeno da computação manifesta-se naturalmente. O
utensílio de nome computador não é mais do que um utensílio, pois serve ao homem
como extensão de suas habilidades para ajudá-lo a viver melhor, seja ampliando os seus
sentidos, a sua capacidade física e mental, estendendo o seu tempo de vida, e assim por
diante. É possível que um pouco da admiração que se tem pelo computador venha do
fato de ele ser uma espécie de máquina-síntese, justamente por ser capaz de simular o
comportamento de qualquer outra, ou de qualquer realidade concebível, seja um
pêndulo simples, uma célula orgânica, um sistema planetário, um sistema de pintura ou
um ecossistema. O computador não existiria sem os conceitos e os métodos da ciência
da computação, e se por acaso existisse não faria muito e não propiciaria essa quase
total interligação das atividades humanas e não criaria, no caso das ciências, o
intercâmbio e a interdependência das áreas de conhecimento nessa escala jamais
imaginada que se tem hoje.
Mas há nisto tudo limites bem palpáveis, uma vez que há uma dimensão da mente para
a qual os atuais sistemas lógicos que dão suporte formal à computação não se prestam.
Para dotar as máquinas de comportamentos mais profundamente humanos, falta à
computação uma infinidade de conhecimentos entre os quais as bases da linguagem do
inconsciente — essa parte do ser que opera sonhos, cria imagens e processa desejos —
tal como descoberto pelo médico austríaco Sigmund Freud e posteriormente estudado
pelo psicanalista francês Jacques Lacan entre outros. No terreno das descobertas
científicas e no fazer artístico, quando os processos criativos são exigidos ao extremo,
há um conhecimento apenas nebuloso e quase nada há de suficientemente formal que
possa ser confiado ao computador, muito embora, às vezes, possa-se até conhecer o
processo, mas não se sabe o que leva alguém a se interessar visceralmente por algo, seja
para trazer à luz o saber científico, seja na concepção única de uma obra de arte. Isto é,
sabe-se como fazer a obra de arte, mas não se sabe por quê, e nem mesmo quando fazê-
la.
Não faz muito tempo, as origens da palavra algoritmo tornaram-se mais do que uma
simples curiosidade quando, em função das muitas hipóteses que circulavam, houve
uma busca sistemática conduzida por historiadores da matemática. Ao cabo de algumas
pesquisas, eles verificaram que algoritmo seria uma corruptela de algorismo que, por
sua vez, era procedente de Abu Ja'far Muhammad ibn Mûsâ al-Khowârizmî, nome de
um matemático que nasceu em Khowârizm (atualmente Khiva, no Uzbequistão) no ano
de 800 d.C. e morreu aos 47 anos, havendo escrito em vida diversas obras sobre
matemática. Uma outra marca involuntária deixada por Al-Khowârizmî encontra-se na
palavra álgebra, ou “reunião de partes quebradas”, que teria origem, não no seu nome,
mas no título de uma de suas obras: Kitab al jabr w’al-muqabala (Regras de restauração
e redução). Atribui-se a ele a idéia de uma escrita para os cálculos em substituição ao
uso do ábaco, numa comunhão entre a matemática e uma espécie de literatura. Muitos
séculos mais tarde, o cientista da computação norte-americano Donald Knuth dá o título
The art of computer programming à sua conhecida obra publicada em 1968, na qual
deixa expressa a consideração de que a escrita de programas, além de científica, é uma
experiência estética do mesmo modo que a poesia e a música.
Primeiros conceitos sobre o raciocínio
A civilização grega amplia os sistemas rudimentares de cálculo dos babilônios com a
compreensão dos aspectos básicos da atividade mental responsável pela dedução, isto é,
a faculdade que os humanos têm de compreender um fato que lhes é desconhecido a
partir do conhecimento de outros fatos. É desta forma que na Grécia de Aristóteles
(384-322 a.C.) nasce a lógica, a ciência que busca representar formalmente o raciocínio.
Na lógica aristotélica, para se fundamentar e justificar uma dada afirmação, parte-se da
afirmação em si e, seguindo-se regras pré-estabelecidas, procura-se chegar aos
elementos dos quais ela deriva. Em caso de não se conseguir sua redução às premissas,
ela será tida como falsa. Esses sistemas dedutivos, que são denominados sistemas
axiomáticos, constituem-se em esquemas formais que sempre estiveram presentes de
uma maneira ou de outra em todo o desenvolvimento das ciências matemáticas. No
primeiro livro da obra Elementos, o geômetra Euclides (330-277 a.C.
aproximadamente), grego como Aristóteles, descreve a geometria por meio de axiomas
e teoremas. Teorema é uma proposição que precisa de demonstração para ser admitida
como verdadeira, enquanto que axioma é uma proposição admitida como verdadeira
sem que a se demonstre.
A computação surgirá como disciplina em resultado do processo evolutivo dessa lógica,
que pode ser chamada filosófica, para uma lógica que encerra a idéia de se transpor o
raciocínio para um processo mecânico baseado em um cálculo formal. E esta lógica
matemática, como é hoje chamada, resulta de metodologias essencialmente diferentes,
uma vez que, por um lado, é puro cálculo e, por outro, em nome de uma busca por
demonstrações rigorosamente corretas, propõe-se a investigar os fundamentos de toda a
matemática.
A lógica matemática propriamente dita se inicia com e matemático alemão Gottfried
Wilhelm von Leibniz (1646-1716), quando o conhecimento matemático já atingia um
nível razoável. Naquela época já se resolviam equações de terceiro e de quarto graus, a
geometria analítica já havia sido proposta pelo matemático francês René Descartes
(1596-1650) e Galileu Galilei (1564-1642), matemático italiano nascido em Pisa, já
notava os primeiros sinais de existência do cálculo infinitesimal, que depois seria
desdobrado pelo matemático inglês Isaac Newton (1643-1727) e pelo próprio Leibniz.
A intenção de Leibniz era estabelecer uma linguagem universal baseada em um alfabeto
do pensamento — a que chamou de characteristica universalis —, uma espécie de
cálculo universal para o raciocínio. Na visão de Leibniz, a linguagem universal deveria
ser como a álgebra ou como os ideogramas chineses, isto é, uma coleção de sinais
básicos que encerrassem noções simples. O matemático inglês George Boole (1815-
1864) deu prosseguimento e expandiu a idéia de Leibniz, tendo encontrado uma
maneira de representar o raciocínio lógico com um sistema matemático. Boole estava
convencido de que a sua álgebra — hoje em dia conhecida por álgebra de Boole — não
somente demonstrava a equivalência entre matemática e lógica, mas também
representava uma sistematização do pensamento humano. Pouco depois, o matemático
alemão Gottlob Frege (1848-1925) procurou separar suas concepções lógicas daquelas
que estavam comprometidas com o desenvolvimento de um cálculo do raciocínio
segundo Leibniz, como eram as de Boole. Frege buscava um sistema que pudesse
romper o domínio da palavra sobre o espírito humano e a ele se deve uma conceituação
exata da teoria aristotélica sobre sistemas axiomáticos, assim como uma clara distinção
entre lei e regra, linguagem e metalinguagem. Com a linguagem, diz-se algo sobre um
objeto qualquer do seu domínio. Por exemplo, com um sistema matemático formal que
contenha toda a aritmética, como o Principia Matemática (PM) de Bertrand Russel e
Alfred North Whitehead (v. descrição adiante), é possível expressar propriedades e
teoremas sobre números, como: “a soma de dois números ímpares é um número par”
(não exatamente assim, em português, mas com os meios lingüísticos de PM, que
incluem um alfabeto de símbolos, regras gramaticais, etc.). Com a metalinguagem,
expressa-se algo sobre os mecanismos de uma linguagem, como, por exemplo, algo
sobre a gramática de PM, ou sobre os seus símbolos e o seu espaço semântico. Quando
a linguagem é a própria lógica, emprega-se a expressão metalógica. Frege é autor da
teoria da descrição e foi ele quem elaborou o conceito de valor e investigou as
características daquilo que o homem diz quando transmite informação por meio de
juízo, dirigindo seus esforços no sentido de provar que toda a matemática seria pura
lógica, e não somente no que referia ao raciocínio empregado nas demonstrações.
Ainda que a aritmética passe a ser compreendida por muitos como o substrato de
qualquer domínio da matemática — em um processo que teve sua expressão máxima
nos chamados axiomas de Peano estabelecidos pelo matemático italiano Giuseppe
Peano (1858-1932) e que passaram a fundamentar toda a aritmética elementar —, o
matemático alemão de origem russa, Georg Cantor (1845-1918), não totalmente
convencido da onipresença da aritmética, procurou investigá-la em maior profundidade
ao imprimir o conceito lógico de classe ao número natural. Cantor, para quem havia
infinitos de diferentes magnitudes, definiu o número em termos de conjuntos, de modo
que a lógica das classes apresentava-se como uma teoria adequada para a investigação
sobre os fundamentos da matemática. Retomando-se o conceito de que computar reduz-
se a contar, que por sua vez significa dotar o número de um nome ou de um símbolo,
quanto mais consistente e completa for esta representação, mais sobre o próprio número
poderá ser conhecido. Desta forma, se os números naturais podem ser gerados por
conjuntos, tudo o que se conhece sobre a teoria dos conjuntos poderá ser aplicado à
compreensão do que é o número, isto além da compreensão comum advinda do teorema
fundamental da aritmética segundo o qual todo número natural pode ser decomposto em
fatores primos.
Raízes tecnológicas do computador
O desenvolvimento das máquinas de cálculo apresenta um extenso hiato que se inicia na
Antigüidade e atravessa toda a Idade Média, de tal modo que nada de significativo
encontra-se registrado entre o surgimento do ábaco e as calculadoras de Blaise Pascal,
em 1642, e de Gottfried Leibniz, em 1673. O ábaco é o mais antigo dispositivo de
cálculo conhecido, um utensílio de contar que é capaz de somar, subtrair, multiplicar e
dividir, sendo originário da Babilônia, provavelmente por volta de 4.000 anos atrás.
Enquanto os ábacos eram tão simples quanto um conjunto de colunas de contas (de
madeira, semente ou pedrinha) perfuradas de modo a poderem deslizar em varetas
dispostas paralelamente umas às outras, as máquinas do século XVII introduziam
facilidades de manipulação e visualização numérica às custas de uma tecnologia
sofisticada, se é que se pode assim dizer. Com o ábaco, a operação de contar, que é a
base das quatro operações aritméticas, vai sendo conduzida deslocando-se as pedrinhas
de um lado para o outro, quando necessário fazendo-se uso da facilidade de casa ou
posição, isto é, o registro de unidade, dezena, centena, milhar. As máquinas de Pascal e
Leibniz eram verdadeiros engenhos e assim possuíam várias peças: um mecanismo para
se introduzir o número; um acionador de adição e subtração; um registrador constituído
por uma série de discos para exibir o valor do número; um mecanismo para lançar e
propagar o “vai-um” por todos os dígitos do registrador; um verificador do
posicionamento de todas as engrenagens ao fim de cada ciclo de adição; e um
mecanismo para zerar o registrador. Desse hiato histórico resta a curiosa constatação de
que, enquanto as máquinas de Pascal e Leibniz existem hoje em dia apenas como peças
de museu, o ábaco continua servindo, sendo ensinado e utilizado no cotidiano de países
do Oriente. E se essas máquinas do século XVII não mais existem porque deram lugar a
outras, pode-se levantar a hipótese de que o ábaco não seria a primeira forma das
máquinas de cálculo, mas sim a sua verdadeira essência, qual seja, a menor
mecanização possível da habilidade de contar.
Em 1822, levando em conta que seria possível manipular a informação por máquina,
desde que se a convertesse em números, o inventor inglês Charles Babbage (1792-
1871), considerado o precursor da construção de computadores, apresentou em Londres
o projeto de um mecanismo que seria capaz de calcular. Teria sido a necessidade por
tabelas de logaritmos mais precisas do que as então disponíveis o principal motivo do
projeto para um dispositivo capaz de resolver equações polinomiais através do cálculo
de diferenças sucessivas, projeto este denominado Difference Engine que valeu a
Babbage o apoio da Sociedade Real e recursos do governo britânico para iniciá-lo.
Mesmo assim, o projeto da máquina de diferenças experimentou um fracasso
tecnológico e não se concretizou devido à enorme distância entre a tecnologia mecânica
da época e a que seria necessária. Contudo, ao lado do fracasso, sobrou a Charles
Babbage uma reflexão valiosa, pois se era possível construir uma máquina que
executasse um determinado tipo de cálculo, seria também possível construir uma que
fosse capaz de executar qualquer tipo de cálculo, pensou ele. Isto é, ao invés de
pequenas máquinas para executar diferentes tipos de cálculo, melhor seria construir uma
máquina cujas peças pudessem executar diferentes operações, bastando alterar a ordem
em que elas interagiam. Nascia aí o conceito de máquina programável. E dessa
compreensão de Babbage, surgiu o projeto da Analitical Engine, a máquina analítica, a
primeira a ter condições de assumir o status de máquina de cálculo universal, uma
noção que será retomada na década de 1930, por Alan Turing, como algo
conceitualmente muito próximo do que hoje se chama computador. De fato, duas
conhecidas características dos programas de computador surgiram com Babbage. A
primeira é o conceito de “transferência de controle”, que permite à máquina comparar
valores e, dependendo do resultado, desviar o fluxo de execução para uma instrução
fora da seqüência. A segunda é a possibilidade do programa alterar-se a si próprio no
correr da execução. Consta que Babbage inspirou-se no tear de Jacquard, uma máquina
construída pelo francês Joseph-Marie Jacquard (1752-1834) que automatizava o
processo de fabricação de tecidos. Para executar um determinado trançado, o tear seguia
um plano — ou programa — que dava ordens a cada operação que deveria ser
realizada, indicando os fios que iam por cima, os que iam por baixo, quando repetir o
processo, e tudo isto sendo comandado por uma série de cartões contendo perfurações
que codificavam o padrão a ser produzido. Nesse episódio da máquina de Charles
Babbage, surge a figura de Ada Augusta Byron (1815-1852), condessa e filha do poeta
inglês Lord Byron, uma mulher cuja história exerce um grande fascínio em todos os que
se interessam pelas origens da computação. Ada tornou-se uma espécie de musa dos
programadores, e não só por ter sido a primeira pessoa a escrever um programa de
computador, mas acima de tudo por ter compreendido a arte da programação ao dar-se
conta da combinação possível entre as funções lógicas e aritméticas na máquina de
Babbage. Conte-se ainda que é dela o conceito de volta (loop) em programação. E, por
fim, talvez como sinal de herança literária, ela teria registrado que “a máquina analítica
tecia padrões algébricos da mesma forma que o tear de Jacquard tecia padrões de
flores”. Isto em 1833, numa época em que não havia facilidades para o cálculo
automático além de uma versão incompleta da máquina analítica de Babbage. Com toda
a importância que se atribui a Charles Babbage, tudo sobre ele e suas máquinas
inconclusas ficaria esquecido por mais de cem anos.
II. A computação como ciência
Em 1900, no correr do II Congresso Internacional de Matemática, realizado em Paris, o
matemático alemão David Hilbert (1862-1943) propôs uma lista de 23 problemas que
viriam a desafiar os matemáticos e a própria matemática. O seu décimo problema, que é
de especial interesse para a história da computação, se enunciava da seguinte forma:
descreva um algoritmo que determine se uma dada equação diofantina do tipo P(u1, u2,
..., un ) = 0, onde P é um polinômio com coeficientes inteiros, tem solução dentro do
conjunto dos inteiros. Trata-se do famoso problema da decidibilidade, tão conhecido
nos meios logicistas que muitas vezes faz-se referência a ele como o
Entscheidungsproblem, em alemão mesmo, independentemente do idioma do falante. O
problema da decidibilidade equivale a perguntar sobre a existência de um procedimento
mecânico efetivo que determine se todos os enunciados matemáticos verdadeiros podem
ou não ser demonstrados, isto é, se eles podem ser deduzidos a partir de um conjunto de
premissas. Hilbert quis demonstrar a coerência da aritmética para em seguida tentar
estendê-la a outros sistemas, investindo com toda a força na criação de uma linguagem
puramente sintática a partir da qual seria possível atribuir a condição de verdadeiro ou
falso aos enunciados. É o chamado sistema formal, que representa o eixo da corrente
formalista, e que mais tarde estimularia Alan Turing a inferir resultados importantes
sobre as máquinas. Hilbert acreditava que a toda afirmação matemática haveria um
procedimento que, após um número finito de passos, parava e indicava se a afirmação
poderia ser ou não ser demonstrada em um sistema formal que contivesse toda a
aritmética. Em 1904, ele articula a chamada metalógica, uma teoria que viria a se
ocupar da independência dos axiomas, da consistência, da completude, e da
decidibilidade dos sistemas formais.
Em 1931, o matemático Kurt Gödel (1906-1978), austríaco de nascimento, publicou
alguns resultados que atingiram profundamente o formalismo de Hilbert ao demonstrar
que todos os sistemas matemáticos suficientemente fortes são inerentemente
incompletos, porque há teoremas nesses sistemas que não podem ser demonstrados nem
negados. Desses resultados, o de maior impacto é o teorema da incompletude — sobre
as proposições indecidíveis — segundo o qual um sistema formal que contenha toda a
aritmética elementar é incompleto ou inconsistente; e mais: a eventual consistência de
um sistema formal não poderá ser demonstrada com apenas os recursos deste mesmo
sistema. Para demonstrar o teorema da incompletude, Gödel — fazendo uso do sistema
formal estabelecido pelos matemáticos ingleses Bertrand Russel e Alfred North
Whitehead na famosa obra Principia Mathematica, que foi publicada em 1915 e que
abrangia toda a aritmética — codificou uma proposição equivalente ao conhecido
paradoxo de Epimênides (“esta proposição é falsa”). A técnica utilizada por Gödel na
demonstração foi substituir os símbolos da proposição por números inteiros que depois
de operados levavam à determinação de um único número que seria uma espécie de
nome para a proposição, de modo que, se uma sentença que proclamava a propriedade
de um número fosse representada por nada mais do que um número, criava-se a situação
em que um número expressava algo a respeito de outro número. E assim poderia até
expressar algo a respeito de si próprio. No sistema de numeração de Gödel, cada
proposição, ou fórmula (ou programa) recebe um número que a identifica e que se
obtém a partir do produto de potências de números primos p1n1. p2
n2, ..., psns, sendo n1,
n2, ..., ns os números atribuídos aos símbolos da fórmula. Uma das fórmulas da lógica
que Gödel utilizou — ~(x ∏ ((~(x (f y))) ∨ (x (0)))) — tem por número o resultado do
produto:
25. 311. 5289. 79. 1111. 1311. 175. 1911. 23289. 2911. 313. 3717. 4113. 4313. 4713. 537. 5911.
61289. 6711. 71. 7313. 7913. 8313. 8913 .
Desta maneira, conhecendo-se apenas o número da fórmula e aplicando-se o teorema
fundamental da aritmética para decompô-lo em fatores primos, toda a fórmula será
recuperada símbolo por símbolo. Conseqüentemente, quando se disser “a fórmula de
número Z é isto e aquilo”, não se tem em Z apenas um identificador que dá nome à
fórmula, mas um nome em cujo interior — o seu esqueleto aritmético — reside a
própria fórmula. Mas a grande vantagem desta codificação, justamente o que
possibilitou a demonstração do teorema da incompletude, é a sua capacidade para
exprimir a auto-referência, pois se uma dada proposição tem como número de Gödel o
inteiro n, e se a proposição em si exprime: “o número n tem a propriedade P”, obtém-se
algo semelhante à auto-referência existente em “esta proposição é falsa”, com a
diferença de que se codifica na verdade algo como “sou uma fórmula legítima da
aritmética mas sou indemonstrável”.
A aritmética é então declarada incompleta justamente porque há proposições, como a de
Gödel, que podem ser escritas com os elementos da própria aritmética sem que se possa
com ela, a aritmética, dizer sobre ou comprovar a veracidade da proposição. Na sua
essência, este teorema de Gödel separa definitivamente o ‘verdadeiro’ do
‘demonstrável’, isto é, se a lógica aspira à representação do raciocínio, ela há de fazer
uma distinção entre o raciocínio mecânico e o humano, pois se em todo sistema formal
consistente, a verdade — às vezes tão banal para os seres pensantes — é uma entidade
que transcende a demonstração, ela haverá de ser considerada como uma espécie de
pacto entre os humanos, e a demonstração o que resta aos sistemas formais para que
ainda assim sejam chamados de incompletos. Decorre da técnica de demonstração
descoberta por Gödel que, se uma demonstração matemática — sempre constituída por
uma seqüência de símbolos — for criteriosamente codificada por números, ela poderá
ser tratada como qualquer outra operação que envolva números inteiros. Isto é, certas
inferências associadas a uma demonstração serão obtidas a partir de propriedades dos
números envolvidos. O teorema de Gödel é certamente um dos momentos mais
iluminados de toda a lógica matemática.
No ano de 1935, Alan Turing (1912-1954), então estudante do King's College, em
Cambridge, Inglaterra, tomou conhecimento do problema da decisão de Hilbert e do
teorema da incompletude de Gödel nas aulas ministradas por um matemático de nome
Max Neumann. Os resultados de Gödel e o problema da decisão motivaram Turing a
pensar em um autômato que pudesse determinar se uma proposição matemática era
demonstrável. Em 1936, ele viria a conceber um dispositivo computacional abstrato,
conhecido hoje em dia por máquina de Turing, que se baseava em um conjunto de
símbolos e na aplicação de regras simples para passar automaticamente de um símbolo
para outro, num esquema semelhante à lógica de Boole. Uma vez que a máquina de
Turing podia receber instruções, isto é, um programa na forma de uma seqüência de
operações codificadas de acordo com o sistema de numeração de Gödel (v. descrição
acima) juntamente com os dados de entrada, ela representava um dispositivo
computacional para fins gerais, e foi o conceito de máquina de Turing que efetivamente
deu início à era dos computadores. Turing preocupou-se em imprimir um conteúdo
matemático à noção intuitiva de procedimento, o que resultou na formalização definitiva
da noção de algoritmo. Um sistema automático, ou autômato, é um dispositivo físico
que manipula automaticamente os símbolos de um sistema formal, sempre de acordo
com as regras definidas no próprio sistema. Para todo sistema formal existe uma
máquina de Turing que pode ser programada para imitá-lo, isto é, para todo
procedimento computacional bem definido, uma máquina de Turing universal é capaz
de simular a sua funcionalidade. A máquina imaginada por Alan Turing consiste de uma
fita de comprimento arbitrário, um cabeçote que é capaz de realizar várias operações
sobre a fita, e um mecanismo de controle do cabeçote que é capaz de armazenar um
programa. A fita é dividida em quadrados, cada um deles ou estará em branco ou
conterá inscrito em si um dos símbolos de um conjunto finito de símbolos. O cabeçote
pode mover-se de um quadrado para outro, podendo ler, escrever ou apagar cada um
deles e pode também mudar de estado entre um dado momento e o seguinte. Qualquer
uma dessas ações é determinada pelo estado interno da máquina e pelo conteúdo do
quadrado sob leitura em um dado instante. Obtém-se a saída da máquina quando ela
pára, a menos que o programa seja uma das proposições indecidíveis de Gödel, caso em
que a máquina não pára. Turing continuou seu trabalho no sentido de demonstrar a
existência de problemas sem solução por computação automática. Destes, o mais
conhecido é o teorema da parada, onde se demonstra que não existe um algoritmo que
antecipe se uma determinada máquina atingirá um ponto final ao rodar um programa,
havendo muitos outros problemas sem solução, mas que foram descobertos justamente
porque eles podiam ser reduzidos ao problema da parada. No contexto de Turing, a
incompletude de Gödel ganha a versão segundo a qual nem todos os enunciados
verdadeiros da aritmética podem ser demonstrados por computador.
Depois dos resultados de Gödel em 1931, outros logicistas além de Alan Turing
buscaram formalizar a noção de procedimento efetivo, havendo todos eles chegado à
mesma conclusão por caminhos diferentes, como a sistematização e desenvolvimento
das funções recursivas de Stephen Kleene (1909-1994) em sua teoria da
computabilidade; o cálculo lambda de Alonzo Church, que mais tarde viria a servir à
linguagem Lisp; e a máquina de Emil Post (1897-1954) e seu sistema de reescrita,
máquina esta equivalente à de Turing. Kleene, em 1936, mostrou que a definibilidade
lambda de uma função — a garantia de que a função é computável, isto é, um programa
que a codifique chegará a um resultado após um número finito de passos para uma dada
parametrização — é equivalente ao conceito de recursividade de Gödel, e também em
1936 Church formulou uma teoria — conhecida hoje em dia por tese de Church — que
estabelece que a recursividade é a própria formalização do que se chama ‘efetivamente
computável’. Recursão é o formalismo segundo o qual uma função recorre a si própria
para computar um dado valor. Um exemplo clássico de recursão é a função fatorial (de
um número natural n): “Se n for igual a 1, o valor do fatorial é 1, caso contrário será
igual ao produto de n pelo fatorial de n-1”. A primeira parte da definição é denominada
cláusula trivial, porque é nela que se inicia o processo de parada. Quando o argumento n
for igual a 1, a função valerá 1, de tal maneira que todos os cálculos pendentes oriundos
da segunda metade da definição — a chamada cláusula de recursão — vão recebendo o
valor numérico que lhes faltava. Assim, por exemplo, quando se quer computar o
fatorial de 3, têm-se os seguintes passos: Três é igual a um? Não! Então o fatorial é
igual a três vezes o fatorial de dois. Como ainda não se sabe o valor do fatorial de dois,
cabe perguntar: dois é igual a um? Não! Então o fatorial de dois será duas vezes o
fatorial de um. Chega-se então à cláusula trivial: Um é igual a um? Sim! Agora,
conhecendo-se o fatorial de um, que é um, pode-se saber o fatorial de dois, que será
duas vezes um, que é igual a dois, e por fim o fatorial de três, que é três vezes o fatorial
de dois, portanto o resultado será seis. A função mdc (máximo duvisor comum) escrita
em Lisp mais adiante é outro exemplo clássico de função recursiva.
O computador em si
Chama-se de geração a cada uma das etapas do desenvolvimento do computador no
século XX. A primeira delas teria ocorrido no período de 1946 a 1958, com ingredientes
que incluíam a máquina de Turing como suporte conceitual, a tecnologia eletrônica e o
peso deixado pela 2ª Guerra Mundial. Mas há antecedentes importantes a serem
considerados. Em 1936, o engenheiro alemão Konrad Zuse (1910-1995), após concluir
que uma calculadora automática necessitava de uma unidade controladora, uma
memória e um dispositivo de cálculo aritmético, projetou o computador Z1, que recebia
o programa por intermédio de uma película cinematográfica e era totalmente mecânico.
Em 1938, ele projetou Z2 usando tecnologia baseada em relés. Concluiu Z3 em 1941, o
primeiro modelo totalmente operacional, contendo mecanismos separados para as
funções aritméticas e uma unidade para converter números decimais em binários. Z3
executava três a quatro adições por segundo e multiplicava dois números em quatro ou
cinco segundos. Foi destruído, juntamente com a casa de Zuse, por um bombardeio em
1944. As máquinas de Zuse não causaram impacto no desenvolvimento geral da
computação, provavelmente em decorrência do isolamento imposto pela 2ª Guerra
Mundial, período em que foram concebidas, mas ele daria uma contribuição talvez mais
importante no campo das linguagens de programação, conforme abordagem mais
adiante.
As dificuldades mecânicas experimentadas desde Babbage deram lugar pouco a pouco a
desafios no campo dos circuitos elétricos, passando evidentemente pela etapa
eletromecânica, isto é, a associação de peças mecânicas com componentes elétricos.
Mas, já na década de 1930, a relação entre os circuitos elétricos digitais e a lógica
formal estava totalmente compreendida, e com isto encontrou-se uma maneira de se
especificarem os computadores com a álgebra de Boole, que compreende apenas dois
valores na sua base de cálculo: 1 e 0. Por exemplo, o valor lógico verdadeiro pode ser
representado pelo número 1 e o valor falso por 0. Claude Shannon, engenheiro norte-
americano, verificou e demonstrou em 1938 que a álgebra booleana era capaz de
descrever o comportamento de circuitos elétricos chaveados — aqueles cujos elementos
possuem apenas dois estados: ou está conduzindo corrente elétrica ou não está, o que é
suficiente para implementar os valores lógicos falso e verdadeiro — e que os
agrupamentos de operações lógicas e aritméticas, próprias da álgebra booleana,
poderiam ser usados na construção de um dispositivo de memória. Consta que Shannon
desenvolveu suas idéias ao trabalhar no projeto do Analisador Diferencial de Vannevar
Bush, um engenheiro eletricista também norte-americano.
A álgebra booleana tornava possível a construção de um dispositivo de “estado” que
podia armazenar a informação, fosse ela um dado ou uma operação. A partir de então,
os computadores digitais puderam ser construídos com base numa tecnologia de
circuitos elétricos que tanto podiam executar operações aritméticas e lógicas quanto
podiam armazenar e atualizar valores parciais das operações. Mais adiante, na descrição
dos conceitos ligados à memória do computador, figuram outros aspectos dessa
correspondência possível entre álgebra booleana e circuitos elétricos.
Além de ter sido o primeiro a empregar válvulas eletrônicas, o computador
desenvolvido em 1939, pelo físico norte-americano John Vincent Atanasoff, na
Universidade de Iowa, confirmou a tendência que já se vinha observando de uma
arquitetura tecnicamente ‘correta’ para os computadores digitais, justamente porque, tal
como em Zuse, havia uma organização compreendendo memória, unidade aritmética e
lógica e unidade de controle, partes estas descritas mais adiante. Entenda-se ‘arquitetura
correta’ em analogia a automóveis tecnicamente corretos, uma vez que os primeiros
automóveis corretos eram como os de hoje, pois tinham motor, quatro rodas e um
dispositivo de direção. Ainda em 1939, Howard Aiken, da Universidade Harvard, e
alguns engenheiros da IBM, deram início ao desenvolvimento de um computador
eletromecânico — o Harvard Mark I — cujas células de memória eram feitas de núcleo
de ferrita, o que viria a aumentar significativamente o desempenho dos computadores.
Esse tipo de memória transformou-se em um marco tecnológico que permaneceria até o
fim da terceira geração nos anos 1970, quando foi substituída pela memória de estado
sólido. Ao ficar pronto em 1944, o computador Harvard Mark I era um gigante de 15 m
de comprimento por 2,4 m de altura, e com ele se encerra a fase que antecede a primeira
geração.
Em 1943, na Inglaterra, numa implementação totalmente eletrônica e sob o mais
absoluto sigilo, concluiu-se o projeto do computador Colossus sob a direção de T. H.
Flowers da Universidade de Cambridge (o próprio Alan Turing participou desse projeto,
mas sem muita influência). Toda a razão de existir do Colossus se justificava em função
da guerra, sendo o seu objetivo único a decifração do código Enigma dos alemães.
Desta forma, como o Colossus era um computador para fins específicos, credita-se ao
computador ENIAC, Electronic Numerical Integrator and Calculator, desenvolvido em
1946 na Universidade da Pensilvânia por J. Presper Eckert e John W. Mauchly, o título
de primeiro computador da primeira geração, justamente por ser uma máquina para fins
gerais, além de ter uma construção totalmente eletrônica. É de certo modo curioso que o
ENIAC detenha esse título sem ter sido um computador de programa armazenado —
um conceito que se tornou sinônimo de computador —, embora se deva considerar que
a palavra geração sempre foi empregada, pelo menos até a quarta delas, para
caracterizar mais a tecnologia eletrônica do que a arquitetura das máquinas. Consta que
John von Neumannn (1903-1957), matemático de origem húngara, propôs o conceito de
computador de programa armazenado quando, em 1945, então pesquisador do Institute
for Advanced Studies da Universidade de Princeton, tornou-se consultor do projeto do
computador EDVAC, que era o sucessor do ENIAC na Universidade da Pensilvânia.
Em junho daquele mesmo ano, juntamente com Arthur W. Burks e Herman H.
Goldstine, ele publica um relatório sobre o projeto (First Draft of a Report on the
EDVAC) onde fica registrada a idéia de uma organização básica a ser cumprida pela
máquina, pelo programa e pelos dados, organização esta que passaria a ser conhecida
como “arquitetura de von Neumann”, e onde ficava estabelecido o conceito de
programa armazenado. Na arquitetura de von Neumann, o programa e os dados são
mantidos indistintamente em seqüência numa mesma memória, de modo que podem ser
processados em um mesmo fluxo de informação, o que não só otimiza a execução, mas
também possibilita ao programa modificar-se a si próprio.
A segunda geração se inicia quando os transistores substituem as válvulas eletrônicas
em 1959. A terceira geração surge nos anos 70 com os circuitos integrados — uma
tecnologia que fabrica em dimensões reduzidas e compactas os circuitos digitais básicos
de um computador em pastilhas de silício. É a chamada integração LSI, ou integração
em grande escala. A quarta geração se inicia nos anos 80 com a radicalização da terceira
geração pela tecnologia VLSI, isto é, a compactação de populações de circuitos digitais
em chips a uma densidade de transistores altíssima, quando esses circuitos deixam a
condição de simples dispositivos lógicos e aritméticos para se tornarem processadores
completos. É aí que surgem os computadores pessoais. A quinta geração foi anunciada
na International Conference on Fifth Generation Computer Systems realizada no Japão
em 1981 como aquilo que viria a acontecer na década de 90. O computador de quinta
geração seria dotado de um processador de conhecimento com o qual realizaria
operações simbólicas. Ao invés de lógica falava-se em conhecimento, sob a alegação de
que grande parte das atividades humanas é de natureza não matemática. Excetuando-se
a engenharia e as aplicações físicas, entendem os idealizadores da quinta geração que a
maneira de se exercer a medicina, a administração de negócios, a advocacia, entre
muitas outras profissões, decorre de inferências e não de cálculos. Acreditam que os
programas que venham a usar as referidas operações simbólicas, ao invés das
numéricas, serão capazes de levar o usuário a adotar o raciocínio automático no seu dia-
a-dia, seja no plano profissional, pessoal, ou de qualquer outra natureza. Os sistemas
especialistas (v. descrição adiante sobre inteligência artificial) são programas que se
constituem numa espécie de modelo para esses processadores de quinta geração. Alguns
desses programas mostraram que uma máquina pode comportar-se tão inteligentemente
quanto um médico ao fazer um diagnóstico, ou um mecânico de automóveis ao avaliar
um motor. Para a quinta geração, declarou-se que a questão central seria o software e
não o hardware, justamente por se imaginar que grande parte do que então era software
passaria a ser hardware, e a tecnologia de chips tinha condições de tornar isto possível.
Mas, termina a década de 1990 e não se vê no mercado esse computador
suficientemente configurado, ao menos no que se refere à qualidade de ser ou de ter um
processador de conhecimento, esta unidade que viria “de fábrica” com capacidade para
comportamentos inteligentes relativamente a certos ofícios, como se dentro dela
existissem à disposição do usuário vários especialistas em diversos assuntos e domínios
de conhecimento e que, portanto, seria programável de uma maneira muito mais
acessível, ao menos para o não especialista em computação.
Arquitetura de computadores
Na sua organização mais básica, um computador divide-se em três partes: CPU,
memória e dispositivos de entrada e saída. No início, esses dispositivos de entrada e
saída serviam apenas para que os programas fossem introduzidos nas máquinas e para
registrar os resultados ou as ações decorrentes da execução do programa. A primeira
forma que o dispositivo de entrada (que lê o programa e o introduz em memória)
assumiu foi a de um arranjo de botões on-off, em que o programador podia pressionar
bit a bit o seu código binário. Depois assumiu a forma de cartões perfurados, conhecidos
desde o tear de Jacquard e as tabuladoras de Hollerith utilizadas no censo norte-
americano de 1890. Também surgiram as fitas de papel perfuradas, que eram as
chamadas “teletipo” usadas em telecomunicações. O mencionado computador Harvard
Mark I fazia uso de fitas de papel perfuradas para a seqüência de operações (o
programa) e de cartões perfurados para a entrada de dados. Em seguida vieram as fitas
magnéticas, que aumentavam de maneira significativa a compactação de dados, isto é,
um maior número de informação relativamente ao tamanho do dispositivo. Mas o
primeiro toque revolucionário veio no início da era dos computadores pessoais, com os
disquetes, que são artigos de baixo custo e permitem o acesso direto ao item
armazenado, diferentemente da fita magnética, que é de acesso seqüencial. Atualmente,
uma época em que se transportam grandes volumes de dados, como certos itens de som
e imagem, o cd-rom, com a sua capacidade razoável de armazenamento, é tudo o que se
tem. Muito se fez até que se tivesse essa disposição de monitor, mouse, teclado,
scanner, unidades de disco e impressora, que é o padrão pessoal ou doméstico de hoje.
Acrescente-se a isto a parafernália de dispositivos próprios de games, como os joysticks,
e ainda os dispositivos para visualização de mundos virtuais, como os óculos de
estereoscopia, que permitem a percepção em 3D de cenas eletrônicas e criam a sensação
de imersão em ambientes sintéticos, dispositivos de interação com objetos virtuais,
como as luvas de sensação tátil, dispositivos para processamento e espacialização do
som, uma técnica que se desenvolveu muito nos últimos anos. Há dispositivos de saída
bastante especializados, como os traçadores gráficos, instrumentos eletrônicos,
interfaces para máquinas industriais e, a rigor, qualquer máquina pode ser conectada a
um computador (até mesmo um outro computador), ou ainda, em qualquer máquina
pode residir um processador, seja uma câmera fotográfica, seja um coração artificial,
casos em que os programas já se encontrarão residentes em memória. Neste exemplo, a
ligação de uma coisa com outra passa a constituir um computador específico, isto é, um
“computador-câmera-fotográfica” ou um “computador-coração”, ao invés de ser um
computador para fins gerais.
Memória é um conceito bastante amplo e sua função é imprescindível ao cálculo desde
a sua forma mais primitiva, o que se evidenciava na necessidade de se guardarem
resultados parciais nas operações aritméticas. Entretanto, o marco que talvez tenha
trazido mais importância a esse dispositivo que guarda informações digitais foi a
introdução do conceito de máquina de programa armazenado, em decorrência da
mencionada sugestão de John von Neumann para se juntarem programas e dados em
memória, uma vez que, a partir de então, o conceito de computador passa a depender
mais do que nunca da noção de memória. Os desafios que surgiram com as primeiras
máquinas eletrônicas fizeram com que se verificasse, especialmente a partir dos
mencionados trabalhos de Claude Shannon, a existência de circuitos elétricos capazes
de “se lembrarem” do valor de uma entrada ocorrida anteriormente. Isto é, se em um
dado momento, um pulso elétrico de um certo nível de tensão for aplicado na entrada de
um circuito como esses, o nível do pulso — que em algumas tecnologias tem o valor
nominal de 5 volts e, em geral, representa o valor binário 1 — será retido pelo circuito,
mesmo depois de cessada a ação do pulso na entrada e enquanto houver alimentação
elétrica no circuito que, desta maneira, realiza uma operação de memorizar uma
informação. Contudo, a necessidade era também por quantidade e não somente por
qualidade, de modo que, muito embora fosse tecnicamente possível, o tamanho dos
relés e das válvulas eletrônicas impedia que se utilizassem circuitos digitais como
células de memória; mesmo uma memória de 1 (um) kilobyte seria algo
desproporcional, tanto em tamanho quanto em custo. Por esta razão, outras tecnologias
foram pesquisadas e empregadas, conforme mencionado na descrição do Harvard Mark
I, o computador que introduziu o núcleo de ferrita como célula de memória, com
milhares de pequenos toros unidos por uma “costura” feita com fios condutores bem
finos, de modo que o conjunto assumia uma forma cúbica. No início dos anos 70,
quando essas memórias atingiram o seu último estágio de aperfeiçoamento, por dentro
de cada um dos seus minúsculos anéis de ferrita passavam três fios: um para sinalizar a
escrita do valor 1 (corrente elétrica em um dado sentido que magnetiza a ferrita) e para a
escrita do valor 0 (corrente elétrica em sentido oposto que desmagnetiza a ferrita), um
segundo para “ler” o conteúdo armazenado, isto é, para “sentir” se o anel de ferrita está
ou não está magnetizado, e um terceiro fio que era utilizado para controle. Em outros
computadores, como o Univac I, que foi construído em 1951, a memória utilizava um
dispositivo a mercúrio denominado linha de retardo, no interior do qual se transmitiam
pulsos sônicos que representavam os dígitos 1 e 0. Finalmente, nos anos 80, a
tecnologia VLSI tornou economicamente viável a construção de memórias que
funcionavam com base na operação de circuitos digitais, uma tecnologia
substancialmente diferente daquela em que as memórias se apoiavam em fenômenos
físicos não processados, como é o caso da magnetização da ferrita e dos pulsos sônicos
em mercúrio. Os circuitos digitais mais simples — também chamados de portões
lógicos (gates em inglês) — são aqueles que têm duas entradas a e b, uma saída c, e a
capacidade de realizar operações boolenas básicas (e, ou e não, além de não_e, não_ou
e ou_exclusivo) com as entradas a e b e dispor o resultado em c. Para se armazenar 1 bit
de informação, o circuito mais básico possível é o circuito digital denominado “trinco”
(latch, em inglês), que é constituído pela associação de dois circuitos não_ou pura e
simplesmente. A operação (a não_ou b) é tal que (0 não_ou 0)= 1; (0 não_ou 1)= 0; (1
não_ou 0)= 0; (1 não_ou 1)= 0, sendo 0 o valor booleano falso e 1 o valor verdadeiro.
No trinco, os portões não_ou são interligados de tal maneira que a saída de um se
constitui em uma das duas entradas do outro. A entradas restantes de um e de outro são
denominadas entradas S (set) e R (reset), que respectivamente escrevem (armazenam) e
apagam um bit na memória a trinco. A rigor, nos portões lógicos, além das duas
entradas que serão operadas, há uma terceira, chamada entrada de clock (v. descrição
adiante em CPU), que recebe um trem de pulsos elétricos de uma dada freqüência, que
torna possível o sincronismo de operação quando uma grande população de circuitos
digitais opera em conjunto, como ocorre nos computadores. Quando se considera o
clock no circuito trinco e mais dois portões e que evitam possíveis ambigüidades nas
entradas, configura-se o circuito denominado “trinco D com entrada de clock”, que é a
forma mínima para uma memória de um bit. O armazenamento do valor do bit, seja 1 ou
zero, é levado a cabo quando o trem de pulsos da entrada de clock estiver no parte mais
alta do ciclo. Por esta razão, o trinco D exige que os pulsos do clock sejam bastante
estreitos. Uma versão mais avançada do trinco D é o chamado flip-flop, onde o
armazenamento do dado ocorrerá na transição da parte alta para a parte baixa (que se
repete a cada ciclo) do trem de pulsos, o que independe da largura desses pulsos. O
passo seguinte na construção de uma memória de estado sólido é o agrupamento de oito
flip-flops, o que constitui uma memória de oito bits, isto é uma memória de 1 byte. E é a
expansão do processo de agregação de flip-flops em pastilhas de silício que permite
chegar-se a uma integração de milhões de células (flip-flops) em um pequeno pedaço de
pedra, que é então envolto com plástico duro e deixando à mostra os pinos de metal que
permitem o endereçamento e o acesso a qualquer uma das células, ou grupo de oito
células, e os pinos que fornecerão o conteúdo da posição endereçada. Memórias
baseadas em circuitos do tipo trinco D são fabricadas em chips e normalmente são
denominadas memórias RAM (random access memory) por serem passíveis de leitura e
escrita. Há outras memórias RAM que, ao invés de flip-flops, usam uma integração de
circuitos que empregam minúsculos capacitores capazes de reter uma pequena carga
elétrica por um curto período de tempo. A diferença fundamental entre as duas
tecnologias é que uma memória a flip-flop permanece no estado em que se encontra
(isto é, “lembra-se” do valor que guardou) por todo o tempo em que o computador
estiver ligado na tomada. É a chamada tecnologia de memória estática. A outra, a que se
baseia em armazenamento capacitivo, é denominada memória dinâmica, porque há a
necessidade de um operação periódica (perto de mil vezes por segundo) para
“resfrescar” o conteúdo da memória, isto é, a ação de um sinal elétrico que recarregue
os capacitadores, caso contrário a fuga de corrente — provocada pelos baixos valores de
capacitância e de resistência elétrica no meio integrado — destruirá a informação.
Existem outros tipos de memória que permitem apenas a leitura do seu conteúdo, que se
mantém gravado mesmo quando a fonte de alimentação elétrica é desligada. Os dados
são inseridos nessas memórias que se denominam ROM (read-only memory) quando da
sua manufatura, dados esses que não serão alterados nem apagados. Em geral, as
memórias ROM servem em sistemas computacionais de programa fixo, como no caso
de processamento dedicado (mencionou-se acima o coração artificial, a câmera, mas há
uma infinidade de outros dispositivos). Existem ainda duas variantes da ROM: a PROM
(programable read-only memory), que permite o usuário gravar (uma única vez) o
conteúdo que quiser em memória; e a EPROM (erasable PROM), que é uma ROM que
pode ser gravada e apagada sem perder as características de ROM.
Nos anos 50, as memórias ainda eram caras e de pouca capacidade. Um computador
típico dessa época, como o IBM 650, tinha cerca de 4 kbytes de memória, e como certos
programas excediam esse volume, criou-se nos anos 60 a técnica de memória virtual
para contornar a impossibilidade de se “rodar” (executar) um programa maior do que a
memória principal do computador, que consistia em se “paginar” (mapear) o espaço de
memória de tal maneira que um dispositivo secundário de armazenamento, como um
disco rígido ou uma fita magnética, pudesse ser anexada à memória a fim de aumentar o
espaço efetivo de memória, tudo isto sem que o usuário percebesse, a não ser,
evidentemente, pelo comprometimento do item tempo de acesso, que é a principal
característica física de toda e qualquer memória. O acesso a uma memória RAM de
estado sólido pode ser da ordem de nanosegundos (10-9 s), ao passo que o tempo de
acesso a um item em um disco rígido pode chegar a alguns milisegundos (10-3 s), isto é,
um tempo 1 milhão de vezes maior. Trata-se de uma técnica que ainda persiste nos dias
de hoje, pois, mesmo quando se dispõe de 256 megabytes de RAM (uma memória
65.536 vezes maior do que a do IBM 650), há aplicativos, especialmente os de natureza
multimídia, que os devoram, não pelo tamanho do código, mas com as suas estruturas
de dados dinâmicas e famintas.
Numa análise mais abstrata sobre a arquitetura de computadores, a memória pode ser
vista como a entidade que encerra a noção de espaço, do mesmo modo que as
velocidades com que as operações são feitas na CPU (unidade descrita mais adiante)
juntamente com as velocidades de acesso para leitura e escrita em memória, encerram a
noção de tempo. Pode-se dizer que o produto espaço×tempo é mais ou menos constante
para uma dada máquina, isto é, quando se tem pouco espaço (de memória) o jeito é
desdobrar as operações em vários passos, o que tornará o cômputo mais demorado.
Quando a situação é extrema, isto é, quando os recursos são compartilhados por muitos
computadores, como em processamento distribuído, caso em que vários computadores
interagem entre si em seqüência ou em paralelo, podendo um dado computador acessar
a memória de um outro, alterar programas e dados, entre muitas outras situações, há
uma complexidade tal na correlação do espaço com o tempo que é preciso recorrer-se a
uma analogia com a relatividade para uma abordagem mais consistente. É neste sentido
que Leslie Lamport, cientista da computação norte-americano, desenvolveu suas
pesquisas, havendo publicado resultados absolutamente originais em 1978 sobre espaço,
tempo e computação, onde fica demonstrado que a ordem relativa dos eventos em
sistemas distribuídos depende do observador, do mesmo modo que em física a ordem
relativa de ocorrência de dois eventos também depende do que se chama observador.
A CPU, que é a unidade central de processamento, constitui-se em uma unidade de
controle, uma unidade lógica e aritmética (ALU) e um conjunto de registradores. A
unidade de controle centraliza a administração da própria CPU, sobretudo no que se
refere ao controle do tempo e do sincronismo entre as operações nos circuitos lógicos,
que são levadas a cabo por uma seqüência de pulsos elétricos interpretados como 1s e
0s. Às vezes, esses números, que são elementos da álgebra de Boole, querem dizer falso
ou verdadeiro. Outras vezes são entidades aritméticas, isto é, são apenas um dígito
binário, ou bit (binary digit), da representação de um número na base 2. E ainda podem
ser apenas um elemento do código binário de um caractere. Um agrupamento de oito
bits é denominado byte, que é a célula da escrita binária, e se constitui na unidade de
medida quando o assunto é espaço para armazenar dados. Desta forma, a capacidade de
uma memória ou de um dispositivo externo de armazenamento é expresso em bytes,
mais comumente kilobytes, megabytes, gigabytes, etc., sendo 1k=210 = 1024. Um
agrupamento de bytes, que se constitui numa unidade de informação de tamanho
padronizado e característico de cada arquitetura, é denominado palavra. Quando, por
exemplo, dois valores tenham que ser somados, o circuito somador da unidade lógica e
aritmética deverá receber a informação de que os operandos — palavras de n bits — já
estão disponíveis nas suas entradas e que ele assim já tem condições de efetuar a adição.
Caso contrário, não se pode garantir que a operação saia correta. Por trás desse controle
do passar do tempo e do sincronismo entre os eventos, encontra-se o chamado relógio
(clock é mais empregado), que é um oscilador digital cuja freqüência é controlada por
um oscilador a cristal. Desta forma, o menor tempo possível para um computador, ou
ainda, a operação mais rápida possível em um computador levará pelo menos um
período do seu clock. O que se chama ALU é a parte onde se encontram os operadores
propriamente ditos, e consta de duas classes de circuitos digitais, os aritméticos, que
somam, subtraem, multiplicam e dividem, e os lógicos, que efetuam as operações
booleanas básicas, como x e y, x ou y, x ou_exclusivo y e não x, Quanto aos
registradores, alguns são indispensáveis, como o IR, ou registrador de instrução, que
contém a instrução da vez e a retém até o final do seu ciclo de execução. Um outro
registrador de suma importância é o contador de programa ou PC, que contém o
endereço (um certo lugar da memória) da próxima instrução a ser executada. PC garante
o seqüenciamento de instruções e possibilita a implementação de mecanismos de
controle de programa como desvio, repetição e chamada de sub-rotinas. Há uma série de
outros registradores internos à CPU, muitos dos quais utilizados no armazenamento de
resultados temporários e de informações de controle. Na execução de um programa, a
CPU encarrega-se de trazer, identificar e executar cada instrução da memória, de modo
que o ciclo de execução de uma instrução conduzido pela CPU compreende vários
passos: primeiro a CPU traz a instrução da memória — isto é, faz uma leitura do
conteúdo existente no endereço de memória indicado pelo conteúdo do contador de
programa PC — e o coloca no registrador de instrução IR. Em seguida avança o
conteúdo de PC de modo que este passe a apontar para a próxima instrução, isto é,
aquela que deverá ser executada ao final desta que está em curso. Logo depois
determina o tipo de instrução que trouxe para ser executada e que se encontra em IR.
Caso se trate de uma instrução que faz uso de dados em memória, localiza-os e os traz
para seus registradores internos. E logo em seguida executa a instrução. Ao final da
execução coloca o resultado em um lugar adequado. E por fim, volta ao primeiro passo
a fim de iniciar o ciclo da próxima instrução.
Há detalhes e aperfeiçoamentos importantes na arquitetura de computadores que aos
poucos se foram descobrindo, como é o caso da microprogramação, uma técnica voltada
para a simplificação do hardware que foi proposta pelo inglês W. Wilkes em 1951 e
implementada pela primeira vez em 1964. No seu desdobramento, a microprogramação
fez surgir o conceito de nível de abstração, ou camada, segundo o qual um computador
deve ser organizado em estágios sucessivos, cada um deles servindo de intérprete do
nível que lhe precede para o que lhe sucede, isto é, de cima para baixo. Com isto, um
programa poderá ser escrito numa linguagem que se pareça mais com o problema do
que com as partes do computador. Por exemplo, quando se precisa obter o máximo
divisor comum (mdc) de dois números (p, q), deve-se dizer à máquina: calcule a divisão
p/q; verifique se o resto r é zero; se o for, pare: o mdc é q; caso contrário, faça p=q e
q=r, e calcule o mdc deste novo par (p, q). E isto, para todos os efeitos, deverá ser dito
aos circuitos lógicos na forma de uma seqüência binária de pulsos elétricos. Como se
vê, há uma distância sintática enorme entre o algoritmo de Euclides tal como escrito
acima e a sua tradução final para o hardware.
Há várias abordagens para o esquema de níveis em uma máquina, mas em todas elas o
chamado nível 0 corresponde à máquina física em si, ou seja, o ambiente eletrônico
organizado e estruturado segundo os padrões da teoria e otimizado segundo uma certa
arquitetura primordial. Ou seja, o nível 0 é o nível da lógica digital, e é o único que
corresponde a uma máquina concreta, todos os outros níveis serão considerados
máquinas abstratas. Isto significa o seguinte: programa-se a máquina física para
transformá-la em uma outra um pouco mais palatável em termos de correspondência
entre a maneira de se dar a instrução e a ação pretendida. Programar no nível 0 significa
dispor de um programa — residente em uma memória onde só é possível a leitura, isto
é, o programa não será apagado e será ele o intérprete do verdadeiro programa a ser
submetido — que amplie o universo lingüístico da máquina desde a forma
extremamente primitiva dos circuitos eletrônicos digitais, onde se dispõe de um alfabeto
de apenas dois símbolos, para algo onde existam facilidades sintáticas com as quais seja
possível dizer “leve este byte para o endereço x de memória”, “verifique se o valor
contido no registrador k é maior do que o valor contido na posição de memória y”, “saia
da seqüência de instruções e salte para a instrução contida no endereço z se o bit mais
significativo do conteúdo do registrador u for zero”. Essa mescla de software e
hardware configura o chamado firmware, e se constitui no interpretador denominado
microprograma. Com isto, estabelece-se o nível 1, onde o programa a ser executado
será dito encontrar-se em linguagem de máquina. Para todos os efeitos, o nível 1 passa
ser a nova máquina, aí então qualificada como virtual e contendo o seu próprio conjunto
de instruções. O conjunto de instruções de uma máquina é o seu repertório de operações
possíveis, qualquer que seja o seu nível. O nível 1, que é o nível de microprogramação,
é capaz de interpretar as instruções do nível 2 no sentido de traduzi-las para uma
seqüência de bits que alimentarão os circuitos lógicos do nível 0. No nível 2, que se
denomina nível de máquina convencional, define-se uma máquina cuja ‘linguagem de
máquina’ é aquela que se define com o microprograma. No nível 3 reside um conjunto
de programas denominado sistema operacional. O sistema operacional, além de
interpretar certas instruções provenientes do nível 4, é responsável pela administração
de tudo o que se passa no interior da máquina, seja para controlar os dispositivos de
entrada e saída, gerenciar o tráfego de programas a serem executados, estabelecer
prioridades, entre muitas outras funções. O nível 4 é o nível da linguagem montadora,
(ou linguagem assembly). O nível montador recebe do nível que lhe é superior — o
chamado nível de compilador — um programa escrito em código objeto. Embora os
endereços de memória no código objeto ainda sejam simbólicos, trata-se de uma forma
que ainda se parece mais com a máquina do que com o problema. O programa montador
trata de construir o código de máquina substituindo os endereços simbólicos por
endereços absolutos, de modo que o nível de microprogramação receba algo
compreensível. O nível 5 é o da linguagem de programação propriamente dita, que será
interpretada pelo compilador para o nível 4. Desta forma, para que um programa seja
executado, haverá entre ele e os circuitos digitais uma cadeia de outros programas que
fazem parte da arquitetura da máquina e a transformam em uma máquina que opera sob
uma linguagem de nível alto. Cada um deles é uma máquina que ‘sabe’ programar a
máquina de nível imediatamente abaixo do seu.
Sistemas operacionais
A exigência mais básica possível para que um programa seja executado é que ele se
encontre na memória do computador. Nos primórdios, além de escreverem os
programas, os programadores tinham também que operar o computador, de modo que
era deles a responsabilidade de carregar os programas em memória desde o meio
externo. Durante a execução do programa, eles tinham que prestar a atenção nas
indicações luminosas do painel para cuidar de qualquer erro ou funcionamento irregular
que por ventura ocorresse. Um sistema operacional é a camada de software que
automatiza a execução de programas, desde a execução pura e simples de um único
programa até o gerenciamento de execução de vários programas (processos) ao mesmo
tempo, quando podem ocorrer situações críticas de compartilhamento de recursos e
agendamento desses processos através de esquemas de prioridade. A evolução dos
sistemas operacionais estende-se desde o estágio do processamento um por um de
programas enfileirados, passando pelos sistemas de compartilhamento de tempo,
quando um mesmo computador passa a atender simultaneamente a um grande número
de usuários, até os atuais sistemas para ambientes distribuídos baseados em tecnologia
de rede. Além da capacidade de multiprocessamento, nos sistemas atuais vários
computadores podem trabalhar de maneira orquestrada em um mesmo problema. Esses
sistemas operacionais — especialmente os da linhagem Unix — estão aptos a uma
administração efetiva e segura em todas as operações da máquina, sobretudo quando
esta se encontra na condição de servidora. Uma servidora é uma máquina que pode ser
alcançada por intermédio de uma rede de computadores (como a Internet) por um
usuário que faz uso de uma outra máquina conectada na mesma rede, mas disposta em
lugar distante. Os serviços mais comuns são websites, bancos de dados, e-mail,
transferência de arquivos, entre outros. Os sistemas Windows da Microsoft, que rodam
nos computadores denominados PC, são os mais populares e “fáceis” de serem
utilizados, mas também são os mais instáveis. As linguagens de programação
contemporâneas passaram a oferecer facilidades sintáticas que permitem ao
programador usar um sistema distribuído de uma maneira bastante simples. É o caso da
tecnologia Corba (Common Object Request Broker Architecture) que permite ao
programador lidar com objetos remotos (isto é, programas e dados que se encontram em
outras máquinas da rede) em seu programa, como se estivesse diante de um único
computador contendo localmente todos os objetos.
Linguagens de programação
As linguagens de programação servem para se falar de homem para máquina, ou seja,
são as ferramentas que se dispõem para a escrita de programas, do mesmo modo que a
língua portuguesa, igualmente a muitas outras, é uma ferramenta para a escrita de
textos, seja uma carta, um artigo acadêmico, a constituição da república, uma peça
literária. De maneira análoga, um programa é também um texto, mas um texto que se
destina a um computador, que o lerá e o interpretará. Para uma máquina, interpretar é
realizar as ações que o programa estabelece. Entretanto, diferentemente dos textos
escritos de humano para humano, os programas de computador não admitem erros
gramaticais, por menor que eles sejam. Por exemplo, é comum (mesmo na mídia) a
seguinte construção: “a gente ama a nossa escola”, o que todo o mundo entende. Mas
um computador que aceitasse o português como linguagem de programação — que é
um estágio ainda não alcançado — exigiria que fosse escrito: “nós amamos a nossa
escola”, ou “a gente ama a escola da gente”. E não exigiria apenas por exigir, mas por
não ter o jogo-de-cintura, ao interpretar um programa, que tem a mente ao interpretar
um texto, já que a mente admite erros de concordância, tempos incorretos em verbos,
vícios de linguagem, ambigüidades e outras deformações, embora essa tolerância ao
erro tenha limites, conforme demonstra aquela situação (anedótica) em que um
forasteiro depois de verificar que todos os dizeres exibidos nas lojas da pequena cidade
continham erros ortográficos, deparou-se com a tabuleta: “Alfaiataria Águia de Ouro”.
Sensibilizado com a correção da escrita, até então a única que encontrara em toda a
cidade, resolveu entrar para cumprimentar o dono do estabelecimento. Ouviu do homem
que então cuidava da loja a informação de que, na verdade, aquilo que estava escrito na
fachada pretendia dizer: “Alfaiataria Agulha (agüia, na pronúncia do povo de poucas
letras) de Ouro”.
Do ponto de vista conceitual, as linguagens de programação possuem três aspectos
formais, quais sejam, o léxico, o sintático e o semântico, e um aspecto informal,
chamado aspecto pragmático, que se refere aos artifícios (ou “manhas”) adquiridos com
a prática de programar na linguagem e também com a troca de experiências com outros
programadores. O léxico é o dicionário da linguagem, ou o lugar onde se define um
conjunto de palavras fixas (denominadas palavras-chaves da linguagem; por exemplo,
if, do, while, etc.) e onde se acrescentam os nomes atribuídos pelo programador a
variáveis, procedimentos e funções. Esses nomes são construídos a partir de regras bem
definidas que são aplicadas ao conjunto de símbolos da linguagem — conjunto este que
congrega o alfabeto, os numerais, os sinais de operações aritméticas, os sinais de
pontuação e os símbolos especiais. Os programadores em geral escolhem nomes
procedentes da terminologia do problema. Por exemplo, em um programa que simula o
funcionamento de um pêndulo simples, é de se esperar que haja variáveis tenham nomes
como “velocidade”, “comprimento”, “fase”, e assim por diante, desde que o
programador seja de língua portuguesa. Encontram-se muitas partes do tipo “if
(velocidade = 0) then acione”, numa combinação de inglês com português que realça o
objetivo pretendido pela proposição, justamente porque todas as ações estarão em
português e só as estrututras de controle estarão em inglês. Isto representa supostamente
uma pequena vantagem sobre o programador de língua inglesa pelo contraste criado na
convivência de dois idiomas, levando o programador a perceber, talvez mais
rapidamente, onde estão as estruturas de controle do seu programa. Os símbolos,
embora sejam os mesmos para várias linguagens, podem ser específicos em certas
outras, como em APL, que requer um teclado alterado. A codificação de caracteres leva
em conta o número total de elementos do conjunto de símbolos. Numa conta
aproximada, o alfabeto acrescenta 26 símbolos minúsculos e 26 maiúsculos, os
numerais acrescentam mais 10, os sinais aritméticos mais 4, e estimando-se em cerca de
90 os sinais especiais, chega-se a um total de 156 símbolos a serem codificados. O
sistema de codificação mais comum para esse universo de símbolos é o chamado Ascii,
que emprega 1 byte para representar um caractere. Como 28=256, sobram apenas 100
códigos, o que não cobriria nem uma parte razoável dos símbolos utilizados nas escritas
algébrica e lógica que são bastante comuns em programas. Um texto no qual o número
π é escrito como pi, como ocorre na maioria das linguagens de programação, tem um
aspecto visual empobrecido relativamente à escrita matemática consagrada e adotada
em documentos impressos. E ainda há na codificação Ascii um problema de ordem
cultural que dificulta a vida dos programadores cujos idiomas têm escrita com base em
outros alfabetos, como o grego, o cirílico, o hebraico, o árabe, entre muitos outros, isto
para não falar dos ideogramas chineses. As entidades de um programa escrito por eles
terão que receber nomes fora do léxico que lhes é comum e familiar. Felizmente,
algumas linguagens atuais, como Java, passaram a adotar o sistema de codificação
Unicode, que faz uso de 4 bytes para cada caractere, e, portanto, com um poder de
representação de 232 = 4.294.967.296 símbolos. Pode parecer um número muito grande,
uma vez que todos os alfabetos e conjuntos específicos de caracteres existentes no
mundo são contemplados e ainda sobra uma enorme reserva, mas é melhor prevenir-se
porque não se sabe o que poderá ser o léxico na computação de amanhã. A boa
programação começa no aspecto léxico, com um critério de escolha de nomes que
acrescente ao programa um potencial literário, por assim dizer, no sentido de facilitar a
escrita correta e de estabelecer um sistema de autodocumentação, isto é, algo que
identifique a intenção de cada parte do programa sem que o programador se utilize de
artifícios como os comentários. Isto decorre de uma diferença básica entre o léxico das
linguagens humanas e o das linguagens de programação. Um escritor da língua
portuguesa terá um conjunto (dicionário) de aproximadamente 150.000 itens para
extrair palavras e com elas compor o seu texto. Ele terá que saber previamente da
existência de toda e qualquer palavra que pretenda utilizar. Nas linguagens de
programação o léxico possui apenas algumas poucas palavras, mas concede ao
programador o direito de criar palavras, desde que ele o faça com o devido respeito a
certas regras, que são próprias de cada linguagem. Entretanto, é no aspecto sintático que
o programador poderá expandir os seu talento de escritor de algoritmos. O sintático
cuida das regras gramaticais para a formação de proposições a partir dos elementos do
léxico e da maneira segundo a qual elas se relacionam e se agrupam. O programa
acabado é uma forma especial de texto que resulta do encadeamento dessas proposições
e, uma vez pronto, ele poderá ser executado, quer dizer, poderá ser encaminhado à fase
semântica. Para tanto, ele deverá ser lido e interpretado por um computador aparelhado
com níveis de abstração tais como os descritos anteriormente, isto é, compilador que o
traduza para a camada assembler, etc. Na primeira etapa dessa leitura o programa é
submetido a uma análise sintática (parsing). Na segunda etapa, caso a escrita esteja
correta, o compilador gera o código objeto, daí de camada em camada até os operadores
do nível lógico. A máquina então executará cada operação que o programa ordena,
sendo que o resultado dessas operações determina os novos caminhos que o programa
vai assumindo. O aspecto semântico da linguagem é o seqüenciamento de ações que
resulta na máquina e o que resulta da máquina ao final da execução ou o que se passa
com ela ao longo da execução do programa.
Existem várias maneiras de se classificarem as linguagens porque há vários níveis de
abordagem. Quanto ao uso, as linguagens podem ser científicas, para fins gerais ou de
domínio específico. Quanto ao tipo, elas podem ser imperativas ou funcionais. Quanto
ao paradigma de programação que elas encerram, as linguagens podem ser
convencionais ou orientadas a objeto. Quanto ao modo de execução de seus programas,
podem ser compiladas ou interpretadas. Quanto à existência de processos simultâneos,
elas podem ser concorrentes ou não. Quanto ao grau de abstração, elas podem ser de
nível alto, nível baixo ou de nível intermediário.
O nível de uma linguagem é considerado baixo quando, ao se ler um programa escrito
com ela, nota-se apenas a existência da máquina. Quando alguém que conheça uma
certa linguagem de nível baixo lê um programa escrito por outra pessoa nesta mesma
linguagem, ele dificilmente conseguirá saber de que se trata, isto é, ele não terá como
separar o problema do programa. (A máquina também não saberá de que se trata, mas
não importa, se o programa estiver escrito corretamente, o problema será resolvido). Em
nível baixo, só um programador familiarizado com a arquitetura da máquina em que a
linguagem foi implementada conseguirá codificar um programa, por menor que ele seja,
mesmo o algoritmo de Euclides. O nível é considerado alto quando a linguagem é capaz
de expressar ou codificar com naturalidade certos problemas, sejam problemas simples
ou complexos. A linguagem Fortran teria sido a primeira de nível alto, uma vez que era
capaz de expressar fórmulas matemáticas numa sintaxe bastante próxima da escrita
algébrica. Quase na mesma época surgiu Lisp como uma outra linguagem de nível alto,
totalmente diferente de Fortran, e voltada para o processamento de listas. Um
matemático, um engenheiro, ou um físico se sentem à vontade com uma linguagem de
nível alto. Fortran é imperativa, ao passo que Lisp é funcional. As linguagens
imperativas são aquelas que trabalham com base na operação de atribuição, isto é, no
acesso à memória repetidas vezes, levando e trazendo dados para cima e para baixo.
Elas são o reflexo da arquitetura de von Neumann. Já a dinâmica de uma linguagem
funcional consiste na aplicação recursiva de funções ou na composição algébrica de
funções. Desde Fortran e Lisp nos anos 50 até os dias de hoje — quando se pode citar
Java como a mais avançada e mais abrangente linguagem para fins gerais da atualidade
— muitas outras linguagens de nível alto surgiram. Destas, muitas desapareceram,
algumas deram lugar a outras, e uma pequena parte sobreviveu. Linguagens de nível
ainda mais alto existem no contexto de suas especialidades, como, por exemplo, na área
de computação gráfica em que se dispõe de PostScript, uma linguagem que computa
uma imagem 2D, dispondo de um amplo conjunto de instruções gráficas que atuam em
vários níveis de detalhe no que se refere a forma, cor, textura e padrões. Um nível
bastante alto se obtém quando o lado formal do idioma falado por alguém é
implementado como linguagem de programação. Embora as técnicas de programação
com linguagem natural ainda não estejam totalmente estabelecidas, uma pessoa de boa
formação acadêmica e conhecedor das estruturas sintáticas do idioma português seria
um bom programador neste nível.
Os dez anos que antecederam Fortran e Lisp já prenunciavam que algo de mais
impactante no campo da programação estava por vir. Em 1945, confinado na Bavária
por causa da guerra e sentindo falta de uma notação formal para a descrição de
algoritmos com a qual pudesse programar o seu computador Z4, Konrad Zuse projetou a
linguagem Plankalkül para codificar formalmente os procedimentos computacionais.
Plankalkül tinha capacidade para tipos, estruturas de dados, atribuição, iteração, entre
outros elementos de programação. Com ela, Zuse codificou um algoritmo de ordenação,
um teste de conectividade em grafos, alguns problemas aritméticos e ainda algo no jogo
de xadrez. Em 1949, John Mauchly lança Short Order Code, — uma linguagem
compilada à mão, mas que se torna a primeira a programar um computador eletrônico,
no caso uma máquina de nome Binac — posteriormente redirecionada para o Univac, o
primeiro computador fabricado em escala industrial. Em 1951, na Remington Rand,
Grace Hopper dá início ao projeto do primeiro compilador, como fora chamado, ainda
que, segundo a própria Grace Hopper, não correspondesse ao que se conhece hoje por
compilador. Tratava-se na verdade de uma biblioteca de sub-rotinas inicialmente
denominada A-0. Ao ser lançada passou a ser chamada de Math-matic. Em 1952, Alick
E. Glennie idealiza um sistema de programação na Universidade de Manchester que se
constituía em um compilador de nome Autocode.
De Fortran a Java
Em novembro de 1954, John Backus, um programador da IBM então nomeado líder do
grupo de pesquisa em programação, publica um relatório (Preliminary Report,
Specifications for the IBM Mathematical FORmula TRANslating System, FORTRAN)
que seria o primeiro documento acerca da linguagem Fortran. Em 1957, a equipe
conclui o compilador da versão I, cujas fórmulas aritméticas podiam combinar valores
de ponto fixo, que é a representação binária estrita para um número inteiro, com valores
de ponto flutuante, que é a versão computacional para a chamada representação
científica de números, isto é, a forma x = m.10e, em que m é a mantissa e e o expoente.
A evolução de Fortran prossegue em 1958 com a versão II, que apresentava a
capacidade de manipular sub-rotinas e podia ligar-se com trechos em linguagem
montadora, isto no computador IBM 704. Ainda em 1958, surge Fortran III
incorporando expressões booleanas e trazendo a possibilidade de nomes de função e de
sub-rotina serem passados como argumentos, além de facilidades para a manipulação de
dados alfanuméricos. E antes que terminasse 1958, Fortran sai do comando de Backus.
Em 1962, a versão IV é lançada por uma outra equipe da IBM, com implementações em
praticamente todas as arquiteturas existentes, especialmente na IBM 709. Backus faz
sérias restrições ao compilador desta versão cuja concepção teria sido adotada
contrariamente à sua opinião. Em 1977, vinte anos depois da primeira versão, fixam-se
as normas ANSI (American National Standards Institute) para a linguagem e lança-se a
versão Fortran 77. Em 1978, um ano após receber o prêmio Turing — o mais
importante da área de computação — John Backus faz restrições à própria linguagem
Fortran, ao declará-la fruto da arquitetura de von Neumann e classificar de lamentável o
fato de que linguagens como Fortran tenham induzido nos programadores um jeito
inadequado de programar. Mas a vida continuava e, a despeito do mea culpa de Backus,
a utilização de Fortran à época da sua declaração ocorria em importantes setores da
tecnologia, de modo que o desenvolvimento da linguagem prosseguia em função de
uma demanda incessante. Com a versão Fortran 90, lançada em 1990, introduziram-se
muitas novidades, como o comando “case” e os tipos derivados, que facilitam o trabalho
do programador. Mas a “alma” de Fortran é e será sempre a mesma encontrada nos seus
primeiros dias: a codificação de expressões algébricas. Em Fortran, o MDC teria a
escrita:
INTEGER FUNCTION MDC(P,Q)
INTEGER R
DO
R = MOD(P, Q)
MDC = Q
IF(R.EQ.0) RETURN
P = Q
Q = R
END DO
END
Uma outra linguagem, radicalmente diferente de Fortran e inteiramente comprometida
com os fundamentos da computação, surgia também em meados dos anos 50 a partir
dos trabalhos do matemático norte-americano John McCarthy. Em um dado momento
de seus estudos em sistemas dedutivos, McCarthy entendeu que as proposições de um
sistema formal voltado para deduções lógicas poderiam ser representadas por listas. E
foi assim que, em 1958, surgiu Lisp, inaugurando a era da inteligência artificial (IA), o
nome dado por McCarthy a esses seus estudos, época em que, juntamente com Marvin
Minsky, deu início ao Artificial Intelligence Project no MIT (Massachusetts Institute of
Technoly). A linguagem Lisp — um nome que é uma referência ao processamento de
listas (List Processing) — caracteriza-se pelos seguintes traços: o cômputo é feito com
expressões simbólicas e não com números. Exemplo: A lista (plus p q) quando
interpretada por Lisp fará com que se calcule a soma dos valores p e q. Em Lisp a
notação é pré-fixada, isto é, primeiro aparece o operador e em seguida os operandos; a
representação de expressões simbólicas é feita pela estrutura de dados denominada lista.
Exemplo: Seja x uma lista de quatro elementos (1 2 3 4), e seja y a lista de três
elementos (plus p q); existe um pequeno grupo de operações seletoras e construtoras.
Exemplo: “car”, que é uma dessas operações seletoras, retorna o primeiro elemento de
uma lista. Deste modo, (car x) = 1 e (car y) = plus. Admite-se a composição de funções.
Exemplo: (plus (car x) 10)). Isto é, (plus 1 10), que valerá 11; usam-se expressões
condicionais quando forem necessárias ramificações para outras funções (v. função mdc
descrita mais adiante); uso de recursão como a ferramenta básica da escrita de funções
computáveis (v. recursão mais adiante); uso de expressões-λ para dar nome às funções
A função defun é a função definidora de funções. Exemplo: (defun d (n) (times 2 n))
cria a função d que, quando aplicada, duplica o valor do seu argumento. Logo após a
definição da função d, poder-se-ia calcular (d 3) = 6; (d 5)=10, etc.; interpretação de
expressões montadas com conectivos booleanos; existência da função eval, que serve
tanto como uma definição formal da linguagem quanto como um interpretador; garbage
collection (coleta de lixo), como o dispositivo básico para o problema da alocação
dinâmica de memória. Trata-se de um mecanismo que automatiza a reciclagem de
células de memória usadas. Após uma seqüência de operações que envolvem estruturas
de dados, muitas células de memória são utilizadas e descartadas como lixo pelo próprio
programa. Em algumas linguagens, como Pascal, o reaproveitamento fica a cargo do
programador, o que representa uma enorme desvantagem, justamente por obrigar o
programador a preocupar-se com aspectos da arquitetura da máquina. Segundo o
próprio McCarthy, algumas das idéias usadas na concepção de Lisp vieram de outras
linguagens, mas a maior parte era feita de idéias novas e ele, McCarthy, sentia desde o
início que essa combinação de idéias resultava em um sistema matemático elegante ao
mesmo tempo em que se constituía numa linguagem de programação bastante prática. A
partir de então, a busca por uma concisão matemática em Lisp se justificava por razões
estéticas e pela crença de que as provas de correção de programas seriam mais fáceis se
a semântica fosse compacta e não apresentasse exceções. Em 1978, demonstrou-se que
os programas Lisp podiam ser representados por proposições da lógica de primeira
ordem. Mas o aspecto mais surpreendente e autêntico de Lisp, que processa apenas um
tipo de dados — a expressão simbólica — é a propriedade que têm os programas de
serem expressões simbólicas do mesmo modo que os dados. Isto significa que um
programa Lisp pode produzir como saída um outro programa Lisp, programa este que
poderá ser interpretado na mesma instância e ao mesmo tempo em que foi criado. E
assim, se o objetivo da inteligência artificial é produzir inteligência, o que de um certo
ponto de vista se resume na síntese automática de programas, esta característica singular
de Lisp — de que programas e dados têm a mesma escrita — lhe confere uma
pertinência singular na codificação do raciocínio, e talvez seja em grande parte por esta
razão que muitos a consideram a linguagem “oficial” da IA. Por fim, é possível com
Lisp a descrição de funções computáveis de uma maneira muito mais concisa, quando
comparada às que se fazem com a máquina de Turing. Sobre este fato, McCarthy em
um de seus artigos tece um comentário bastante curioso, segundo o qual a dificuldade e
o jeito deselegante de se programar uma máquina de Turing não incomodam os teóricos
das funções recursivas porque dificilmente eles terão que codificar uma dada função,
uma vez que a teoria se preocupa com os aspectos genéricos das funções recursivas. Em
Lisp, o algoritmo de Euclides seria assim escrito:
(defun mdc(p q) (cond ((eq (remainder p q) 0) q) (t (mdc q (remainder p q)))))
Isto é, um programa que ordena ao computador o seguinte: defina (defun) a função de
nome mdc que tem dois argumentos p e q. Se (cond) o resto (remainder) da divisão de
p por q for igual (eq) a zero termine o cômputo e retorne q como o valor do mdc. Caso
contrário reaplique (o que se denomina recursão) a função mdc de tal maneira que o
argumento p seja igual a q e o argumento q seja igual ao resto de p por q.
Entre as linguagens funcionais derivadas do ramo Lisp, além dos dialetos da própria
Lisp, destacam-se APL, desenvolvida por Kenneth Iverson em 1962 na IBM, que é
famosa pelo seu elevado poder de concisão na escrita de algoritmos e pela incorporação
de vários conceitos matemáticos, e Snobol, uma linguagem voltada para o
processamento de cadeias de caracteres (strings), que foi desenvolvida em 1964 na Bell
Labs por Farber, Griswold e Polonsky. Com suas facilidades para verificação de
casamento de padrões, Snobol (StriNg-Oriented symBOlic Language) foi bastante
utilizada na área da demonstração automática de teoremas.
Fortran e Lisp deram origem a diferentes linhagens no processo evolutivo das
linguagens que depois de um certo tempo passaram a se combinar na base de uma
aproximação entre o que havia de melhor, mais forte, e mais consolidado em ambas as
linhagens. Entre 1958 e 1968, desenvolveu-se a linguagem Algol como uma
descendente de Fortran. O próprio nome fazia referência a uma “linguagem
algorítmica”, isto é, algo melhor do que Fortran quanto à escrita de algoritmos. Um
relatório elaborado em 1963 pelo dinamarquês Petr Naur sobre Algol introduz a noção
de “semelhante a Algol” (Algol-like) para designar toda linguagem que apresente as
seguintes características: é algorítmica, no sentido de poder descrever processos
computacionais; é imperativa, uma vez que o transcurso do algoritmo que ela venha a
encerrar será feito de acordo com uma seqüência de acessos, leituras e escritas na
memória; suas unidades básicas de programação são o bloco e o procedimento; encerra
as noções de tipo e de verificação de tipo; faz uso da regra de escopo léxico; e, por fim,
é compilada. Dentre as “semelhantes a Algol” mais conhecidas, destacam-se Pascal, que
foi desenvolvida de 1966 a 1968 pelo cientista da computação suiço Niklaus Wirth, e
Simula, uma linguagem que traria importantes conceitos de programação — como o
conceito de classe — desenvolvida em 1965 na Noruega por Kristen Nygaard e Ole-
Johan Dahl e que viria a ter como descendente mais conhecida a linguagem Smalltalk.
Com Pascal, foram introduzidos os conceitos de parsing de precedência simples, os
tipos record e pointer, o comando case, e nascia também um maneira mais estruturada
de se programar. Pascal viria a ter grande aceitação nas décadas de 1970 e 1980,
especialmente nos meios acadêmicos, tornando-se não apenas a linguagem de trabalho
nos cursos de ciência da computação, mas também sendo adotada como
pseudolinguagem “oficial”, isto é, aquela linguagem que se utiliza para a descrição de
algoritmos em livros, aulas, teses, e onde quer que haja um texto sobre computação.
Com Smalltalk inaugura-se a era da programação orientada a objeto, que se constitui na
técnica de programação mais eficiente da atualidade. Smalltalk, uma linguagem criada
em 1972 por Alan Kay na empresa Xerox Corporation, embora tenha a influência de
Lisp nas suas estruturas mais internas, é inteiramente baseada na noção de classe
estabelecida em Simula. A idéia de orientação a objeto, embora já implícita em Simula,
foi verbalizada e surgiu a partir de uma analogia biológica elaborada por Alan Kay, um
cientista da computação graduado em matemática e em biologia, segundo a qual os
objetos — isto é, as instâncias de cada classe — seriam como células de organismos
vivos, que se comunicam entre si por mensagens químicas. Com a orientação a objeto,
as técnicas de programação saem de um nível molecular, ou mesmo atômico, para um
nível celular, de modo que certos problemas considerados complexos, que em geral
demandam uma equipe numerosa de programadores, podem ser abordados por uma
única pessoa que domine suficientemente esta maneira de programar. Mais do que isto,
a programação poderá ser praticada por pessoas em diferentes graus de formação e
habilidades, como por crianças, adultos semi-analfabetos, deficientes físicos, e até
mesmo deficientes mentais. A propósito, um dos experimentos conduzidos por Kay com
Smalltalk visava à formação de crianças programadoras, o que de certo modo viria a ser
também uma tentativa de demonstrar que a programação é uma habilidade natural do
ser humano. Nos anos 80, a Digital Equipment Corporation desenvolve a linguagem
Modula-3 como uma descendente de Pascal, dotada de concorrência, orientação a objeto
e trazendo interessantes mecanismos para a manipulação de exceções, como a
verificação de erros durante a execução.
A linguagem C sucede a linguagem B, que teve a influência de BCPL — uma
linguagem funcional criada por Martin Richards. C, embora seja uma linguagem para
fins gerais, foi desenvolvida na Bell Labs em 1972 por Dennis Ritchie para que o
sistema operacional Unix que se destinava ao computador PDP-11 fosse escrito numa
linguagem de nível alto. Na verdade, o próprio Ritchie admite que C é uma linguagem
de nível relativamente baixo, uma vez que as suas estruturas de controle são as mais
simples, assim como as suas estruturas de dados primitivas são as mais básicas. Esta
minimidade traz como benefício maior o emprego de um compilador de porte pequeno,
capaz de gerar um código de máquina eficiente, e, portanto, podendo adaptar-se em
diferentes arquiteturas sem maiores dificuldades. Este fato, associado à proximidade que
existe entre C e Unix, impôs, no bom sentido e no mau também, a utilização de C ao
extremo, mesmo quando a metodologia correta de programação apontava para o uso de
uma outra linguagem de programação. Todos os problemas eram resolvidos em C, o que
no final das contas acarretava uma uniformização da técnica de programar que nivelava
por baixo, desde que C é reconhecidamente uma linguagem de nível baixo. O
desdobramento desse quadro será lento entre os anos 70 e 90, justamente porque o
processo haverá de levar em conta que muitos investimentos foram feitos em C, e que
os argumentos advindos da esfera econômica são mais determinantes do que os
argumentos científicos. O primeiro passo na alteração desse quadro foi dado com o
surgimento da linguagem C++, idealizada por Bjarne Stroudtrup em 1980, também na
Bell, com o que ele denominou de “C com classes”, transformando C numa linguagem
orientada a objeto ao mesmo tempo em que a mantinha tal como ela era. Isto é, um
programa escrito em C poderia ser interpretado em C++. Desta forma, a partir da
primeira implementação de C++ em 1983, os grandes investidores de sistemas escritos
em C estavam aptos a buscar estratégias de programação mais avançadas, aos poucos,
como era conveniente, sem ter que largar tudo o que haviam desenvolvido e recomeçar
do zero. Numa linha conceitualmente mais pura do que C++, por volta de 1982-1983
surge a linguagem Objective-C, desenvolvida por Brad Cox e Tom Love, que expande a
linguagem C com a introdução de alguns recursos como classes, mensagens e herança,
todos eles baseados em Smalltalk.
Hoje em dia, época em que os computadores pessoais são um item quase tão básico
quanto uma escova dental, ao menos para quem passou a freqüentar a aldeia global da
Internet e a utilizar os seus novos meios de comunicação, o conceito de hipertexto é
posto em prática por programas denominados browsers, que se constituem em interfaces
de leitura de hipertextos. Hipertexto é um documento que contém imagens, sons, textos
e uma entidade denominada link, que amplia a dimensão de leitura, no sentido de que a
partir deles pode-se alcançar com o browser qualquer ponto do mesmo documento ou
de um outro documento, mesmo se estiver em um outro computador. O arquivo de texto
que concretiza um hipertexto é fornecido por um computador servidor a um computador
cliente por intermédio dos mecanismos de comunicação da rede. Para isto, uma máquina
que dispõe de um posto de documentos — ou site — tem que manter em funcionamento
ininterrupto um programa que atende a todas as solicitações de leitura do documento
que se fizerem, e sempre que houver uma solicitação, o programa servidor de páginas é
acionado, de modo que o hipertexto, devidamente codificado em uma linguagem de
formatação de hipertextos, como a atual HTML (HyperText Markup Language), será
transferido segundo os modos de transmissão da Internet desde o servidor até o browser
do computador cliente, onde será interpretado e exibido ao leitor. Os projetistas de Oak
— uma linguagem desenvolvida por James Gosling no início dos anos 90 na Sun
Microsystems —, preocupados com objetivos outros que não a Internet, partiram da
sintaxe e da semântica de C, adicionando cautelosamente algumas características de
C++. Eles começaram por eliminar certos pontos complexos e conceitualmente
problemáticos, como a herança múltipla, que é uma propriedade que possibilita a
existência de objetos descendentes de várias classes a um só tempo, o que descaracteriza
e não segue a analogia entre objeto e células vivas que se estabeleceu em Smalltalk. Aos
poucos, a equipe da Sun, com vistas em uma linguagem puramente orientada a objeto,
enxuga a estrutura sintática de C++, diminui a sua extensão semântica e, depois de uma
seleção criteriosa, agrega mecanismos pertencentes a outras linguagens de reconhecida
excelência.
Desta maneira, introduziu-se em Oak o gerenciamento automático de memória por
coleta de lixo com base em Lisp (v. descrição de coleta de lixo acima, na parte de Lisp);
a verificação de erros durante a execução com base em Modula 3, que torna os
programas mais robustos, uma vez que um erro na fase de execução (uma divisão por
zero, por exemplo) não necessariamente encerrará a execução do programa, e um
programador experiente de posse desses meios saberá o que fazer para que o programa
sempre encontre uma saída; a avaliação dinâmica de programas definidos por
programas como em Lisp, isto é, se a saída de um programa for um outro programa, é
possível que este seja executado na mesma instância do programa que o gerou; os
processos concorrentes como em Mesa, isto é, a possibilidade de ações simultâneas
dentro de um mesmo programa; a definição de interfaces como em Objective-C, que
possibilita o aproveitamento de definições feitas em classes fora da sua ascendência;
suporte para objetos distribuídos, que a torna própria para ambientes de rede; e ainda
objetos para interfaces gráficas. Por fim, criaram o conceito de arquitetura neutra, que
viria a ser o grande trunfo da linguagem no quesito portabilidade. O alvo inicial de Oak
era o mundo dos dispositivos de controle para utensílios residenciais, como a TV a cabo
e outros eletrodomésticos (dizem que até as torradeiras), alvo este que não chegou a ser
atingido, o que fez o projeto Oak entrar em declínio em 1993. Mas, uma decisão
estratégica da Sun em 1994 viria a ressuscitá-lo para uma missão revolucionária, pois
nada menos do que a Internet teria que ser o ambiente principal de atuação da
linguagem Oak. Desta maneira, o desafio inicial era fazer com que os programas
escritos em Oak rodassem em ambientes de processamento distribuído nos quais um
programa originado em um certo computador pode ser transferido pelos protocolos da
Internet, como um todo ou em partes, para outros computadores, em um esquema de
cooperação onde cada computador processa o que lhe cabe, isto é, executa uma
determinada parte do programa ou todo o programa. Daí porque processamento
distribuído significa em essência o ajuntamento do esforço computacional de pelo
menos dois computadores. A proposta da linguagem Oak era fazer uso do
processamento distribuído para dar vida aos hipertextos veiculados na Internet, de modo
que as páginas escritas na linguagem HTML poderiam conduzir programas Oak até a
máquina do leitor onde seriam executados na Máquina Oak Virtual instalada no
browser. Os documentos de rede deixavam de ser páginas estáticas de livros para se
tornarem verdadeiros programas, isto é, documentos parametrizáveis pelo leitor, com
capacidade de se adaptarem aos interesses dele, na forma de programas multimídia,
realidade virtual e muito mais. Em janeiro de 1995, Oak muda de nome e torna-se Java,
um nome que se espalhou pelos quatro cantos do mundo a uma velocidade jamais vista.
Ainda em 1995, a grande indústria de informática investe em novos formatos e em
novas APIs que dotarão Java (cinco anos mais tarde, diga-se de passagem) de meios que
possibilitarão a animação e a interatividade, especialmente na área de renderização 3D e
multimídia na Internet. Essas tecnologias permitem a integração e o fluxo de áudio,
vídeo e grafismo 3D em tempo real; espacialização do som; e facilidades para vídeo
conferências.
Fig. 1. Cena de um mundo sonoro criado pelo programa Cubismo (A. Arcela, 1999)
Java apresentou-se ao mundo como uma linguagem simples, orientada a objeto,
distribuída, interpretada, robusta, segura, neutra do ponto-de-vista da arquitetura, de alto
desempenho, concorrente e dinâmica. Simples porque é fácil de ser assimilada e por ter
uma aparência familiar. Orientada a objeto, por basear-se nos quatro princípios desse
paradigma de programação; quais sejam, abstração, encapsulamento, herança e
polimorfismo, conforme descrição anterior de Smalltalk e de C++. Distribuída por
admitir aplicações na rede e por suportar vários níveis de conectividade. Interpretada
porque seu compilador gera bytecodes ao invés de código nativo (os bytecodes se
constituem na linguagem de máquina da Máquina Java Virtual, que é formada pelo
interpretador e pelo seu sistema de runtime). Robusta porque elimina certos erros de
programação. Segura porque protege o sistema de arquivos do leitor quando um
programa escrito em Java procedente de algum ponto da rede for eventualmente
executado no seu computador. Neutra do ponto-de-vista da arquitetura porque a
Máquina Java Virtual pode ser implementada em qualquer plataforma. Portável porque,
além da característica de arquitetura neutra, não há em Java aspectos dependentes de
implementação. De alto desempenho porque, mesmo sendo uma linguagem
interpretada, apresenta um desempenho entre as compiladas de nível baixo e as
interpretadas de nível alto. Concorrente porque tem meios de suportar os threads, que
são os processos leves.
Na terminologia Java, applets (ou mini-aplicativos) são programas que se veiculam na
rede e que são executados no computador cliente, enquanto que aplicativos são
programas locais que se executam fora do contexto de rede. A organização do espaço de
classes em Java é feita por pacotes (packages), de maneira que um pacote é um conjunto
de classes, e um grupo de pacotes constitui uma API, que é a abreviatura de Abstract
Programming Interface. Há um grupo de APIs que forma o núcleo do sistema Java,
cobrindo praticamente todas as áreas da computação consideradas básicas, como redes,
bancos de dados, interfaces de usuário, segurança de dados, processamento de texto,
construção de compiladores, compactação de dados, entre outras. E há um grupo
complementar — de APIs especializadas — que está disponível no site da linguagem,
além de uma quantidade extra de APIs escritas por outras empresas, pesquisadores e
indivíduos mundo a fora. Entre as APIs complementares, encontra-se a suite JavaMedia,
contendo o que há de mais atualizado em tecnologia multimídia para integrar áudio,
videoclipes, animações, fontes de caracteres, grafismos, imagens, entrada e saída de voz
e modelos 3D. As artes computacionais, especificamente a composição musical
algorítmica, a síntese de imagens e a construção automática de mundos virtuais (fig.1),
são beneficiadas de modo considerável com JavaSound, Java3D e JavaSpeech, que
representam um nível tal de implementação de facilidades e conceitos que propiciam
uma extensão considerável da linguagem artística em todas as suas dimensões. Inúmeras
outras atividades fazem ou podem vir a fazer uso dessa tecnologia multimídia, como a
educação, a pesquisa científica, a indústria em geral, os serviços e a indústria de
software que atua no front dos jogos, suas batalhas, agentes e lucros.
III. As Últimas Fronteiras
É possível que a inteligência artificial represente hoje em dia a parte mais visada da
computação e também se constitua no assunto de onde mais se espera, talvez porque o
seu progresso venha ocorrendo a uma velocidade inferior àquela que a expectativa
social foi levada a supor. Expectativa esta feita de sonhos e mitos por uma tecnologia
perfeita, que são próprios da natureza humana, e também com a ajuda expressiva da
mídia e sua capacidade de alimentar fantasias.
No final dos anos 60, o cinema sugere para o ano 2001 nada menos que o computador
HAL-9000, uma máquina que, além de ver, ouvir, e falar, sabia lidar com a alma
humana, como se registrou na famosa cena de chantagem emocional esboçada por HAL
em um apelo que até hoje ressoa em ouvidos mais impressionados: “você tem noção do
que está fazendo?”, pergunta ele em tom de submissão, dirigindo-se ao astronauta Dave.
Em seguida, enquanto o homem prossegue desconectando os circuitos inteligentes, a
máquina insiste em tentar demovê-lo: “pare, Dave; estou com medo!”. Era o
computador pedindo clemência para não ser desligado, e o fazia como se tivesse noção
do que era a morte. Em um texto sobre computação não se deve ir muito além de 2001,
uma Odisséia no Espaço, isto no terreno da ficção científica, e não só porque as
distâncias entre ficção e suas perspectivas de realização vão ficando além dos
argumentos básicos que um simples autor pode dispor, mas também por se entender que
dificilmente haverá um outro filme que o supere na dramatização da relação homem-
máquina, ao menos em caracterizações nas quais a máquina tem “cara” de máquina.
Evidentemente que a arte do autor de ficção é ampliar ao máximo essa distância ao
mesmo tempo em que nutre a tal perspectiva de realização. Em filmes como Blade
Runner e em outros feitos nos anos 80, a robotização é extrema, de modo que não há
distinção visual nem comportamental entre humanos e máquinas. Quando comparada à
ficção, a inteligência artificial feita com os métodos computacionais de hoje é
obviamente primária. Mas, relativamente a outros domínios da própria computação,
trata-se de uma técnica sofisticada de programação e que tem trazido alguns poucos,
mas bons resultados de ordem prática. Os artefatos mais comuns da IA são os seguintes:
Redes neurais: Sistemas que produzem inteligência por meio de simulação do
funcionamento neural e de suas conexões. Aplicados em reconhecimento de voz e em
processamento de linguagem natural, os programas possuem variáveis (pesos) que
necessitam de uma adaptação à situação a que se destinam. Por exemplo, se uma rede
houver sido construída para identificar a voz de uma dada pessoa, ela deverá ser
“treinada” no sentido de que se alterem progressivamente e de maneira convergente os
tais pesos a fim de que o desempenho e a confiabilidade de reconhecimento sejam
aprimorados.
Vida artificial: Em linhas gerais, estuda a vida biológica pela síntese de vida artificial. A
tecnologia de vida artificial fundamenta-se na teoria da evolução, e em ecologia. Faz
uso de uma metodologia computacional denominada algoritmos genéticos, que são
procedimentos de busca que usam a mecânica da seleção natural e conhecimentos de
genética.
Jogos: Programas tradicionais em computação como se demonstra pela existência de
programas que jogam xadrez desde Konrad Zuse. Muito embora o xadrez seja o jogo
mais estudado e o mais implementado — não se pode esquecer que o programa Deep
Blue da IBM derrotou o campeão mundial, o russo Garry Kasparov —, os verdadeiros
campeões de audiência são os chamados jogos de estratégia e são eles os que mais se
beneficiam das técnicas de IA. (No filme The Matrix, lançado em 1999, os participantes
do jogo transferem-se para dentro no programa e passam a lutar diretamente, isto é, na
base do corpo-a-corpo, com os agentes virtuais, numa alusão explícita a Alice no país
das maravilhas, obra de Lewis Carroll).
Sistemas especialistas: Programas que substituem profissionais humanos e tomam
decisão em situações reais. Há sistemas especialistas que atuam na área médica (no
diagnóstico de doenças e na prescrição de medicamentos), na interpretação de imagens,
no controle de tráfego aéreo, na área de ensino e treinamento, e em muitas áreas onde o
perfil do profissional é bem conhecido. Um sistema especialista lida com a
especificação de um problema e um objetivo a ser alcançado. Para isto, constitui-se de
uma base de conhecimento e de um mecanismo de inferência.
Robótica: Máquinas computacionais complexas que podem ver, ouvir, deslocar-se e
reagir a estímulos sensoriais. De certo modo, a robótica se constitui na resposta
tecnológica à necessidade e ao direito do homem de ser substituído em certas
circunstâncias pela máquina, e não só nos serviços pesados e perigosos, mas em todas as
situações que caracterizem a sua submissão a algo ou a alguém. Muito embora nos dias
de hoje existam formas intermediárias de submissão, sempre em decorrência da
desigualdade social, o bom senso aponta para um mundo sem esses problemas que ainda
afligem os povos, como a fome e a miséria, e cuja persistência evidencia um enorme
despreparo da humanidade. Chegará o dia em que isto será um mal tão superado quanto
uma doença extinta por uma vacina e um programa de saúde pública, e aí se abrirá
espaço para que, inicialmente nos setores produtivos, os robôs se integrem de tal
maneira nas atividades humanas, que se tornem seus concidadãos, por assim dizer. As
ficções são levadas a incluir uma abordagem sociológica específica como uma
conseqüência imediata de que humanizar a máquina traz o ônus de se pensá-la como um
ser social.
Agentes: São entidades computacionais autônomas que agem em nome de entidades
humanas. São como os robôs, com a diferença de que são feitos apenas de software e
não de parafusos e chips como os verdadeiros robôs, ainda que no campo dos mundos
virtuais, os agentes, além da “alma”, possam também receber um corpo — uma
modelagem geométrica em 3D — e, portanto, um rosto que lhes confere uma identidade
visual. O agente “trabalha” no sentido de fazer algo ou de reagir a algo, sendo capaz de
aprender e de cooperar. No mundo virtual BR, um projeto que se iniciou em 1997 na
Universidade de Brasília, os agentes são habitantes virtuais (fig. 2) que se destinam a
auxiliar os visitantes reais, sendo eles construídos à imagem e semelhança de brasileiros
que viveram ou que vivem no período de tempo no interior do qual se encontra o
visitante. Os agentes de BR estão sendo programados para guiar e orientar o visitante na
assimilação dos objetos desse mundo que retrata a história da inteligência brasileira por
intermédio da exibição 3D da produção intelectual em arte, ciência e tecnologia, desde o
ano de 1500 até o presente.
Fig. 2. Cena do programa Mundo BR com agente do século XVI (modelagem de N. Kerinska, 2000)
Compreensão de linguagem natural: Talvez o problema mais difícil de toda a
inteligência artificial. São programas que interagem com o usuário na sua linguagem
nativa, como o português que se fala no Brasil, por exemplo. No atual estágio dessa
tecnologia, apenas os textos relativamente simples e diretos são assimilados pela
máquina, de modo que já existem produtos que permitem um certo nível de diálogo
verbal com o computador, seja para editar um texto, jogar um game, navegar na
Internet, dirigir um automóvel, uma aeronave ou um satélite. Já nos casos de densidades
semânticas extremas, como a poesia, as distâncias entre máquina e texto tornam-se
astronômicas. Por exemplo:
“Trocaica te amei, com ternura dáctila
e gesto espondeu.
Teus iambos aos meus com força entrelacei.
Em dia alcmânico, o instinto ropálico
rompeu, leonino,
a porta pentâmetra.
Gemido trilongo entre breves murmúrios.
E que mais, e que mais, no crepúsculo ecóico,
senão a quebrada lembrança
de latina, de grega, inumerável delícia?”
Leituras sucessivas confirmarão uma certa dificuldade na compreensão do que diz este
poema, mas aos poucos surge a impressão de que ele vai sendo assimilado ainda que
não se saiba o significado de suas palavras principais. Quando se recorre a um
dicionário da língua portuguesa, como o Novo Dicionário Aurélio da editora Nova
Fronteira, constata-se que trocaico é um verso cujos pés (unidade métrica) são
constituídos de uma sílaba longa e outra breve; dáctilo é um pé de verso formado por
uma sílaba longa seguida de duas breves; espondeu é um pé com duas sílabas longas;
iambo, pé com um sílaba breve e uma longa; alcmânico, verso composto de três
unidades dáctilas e uma cesura, que é a última sílaba de uma palavra que inicia o pé de
um verso grego ou latino; ropálico, verso que começa por monossílabo, tendo cada uma
das palavras seguintes uma sílaba a mais que a palavra anterior; leonino, verso em que
os hemistíquios rimam; pentâmetro, verso de cinco pés, composto de dáctilos ou
espondeus e uma sílaba longa, mais dois dáctilos e uma sílaba longa ou breve; trilongo,
verso que tem três sílabas longas; ecóico, verso cujas duas últimas palavras findam com
vogal idêntica. Ou seja, a consulta ao dicionário não altera aquele significado global que
surgiu sem o conhecimento dessas palavras que nada significam a não ser figuras
métricas.
Um programa não muito bem preparado poderia vir a compreender este poema
intitulado A paixão medida, de Carlos Drummond de Andrade, da seguinte maneira: o
“dia alcmânico” teria sido a situação vivida em três etapas subdivididas cada uma delas
em um espaço de tempo maior e dois menores. O instinto, por ser ropálico, crescia
pouco a pouco. Já um programa de melhor nível poderia admitir que “dia alcmânico”
foram momentos sentidos com a sensibilidade para o verso homônimo, pois assim eles
se passaram. O gesto espondeu se fazia em pares de movimentos persistentes, e assim
por diante. Contudo, quanto mais se aprimoram métodos que valorizam o significado
das palavras, menos se chega à compreensão que a leitura humana é capaz de alcançar,
justamente porque a leitura humana procura, e de alguma maneira encontra, o enfoque
que captura a real intenção do autor, qual seja, a exploração não só da métrica da
palavra cujo conteúdo quer dizer métrica, mas sobretudo da sua sonoridade, que sugere
outra palavra, de significação comum. Com “ternura dáctila e gesto espondeu”, o amor
fora mútuo, porque trocado. Ternura delicada da ponta dos dedos. Gesto espontâneo.
Instinto crescente que o transforma em leão para romper a porta da delícia. E que mais?
O que leva a leitura humana a esta curiosa, mas legítima interpretação, é uma indagação
para a qual o presente texto não tem uma resposta, muito embora possa supor, talvez ao
estilo da psicanálise, que a tentativa do leitor de identificar-se com o autor é o que o
leva à interpretação correta ou, quem sabe, o desejo de ser ele mesmo o autor daquilo
que lê. De uma forma ou de outra, os modelos computacionais para a mente estão
aquém desta capacidade, ainda que não o sejam por uma impossibilidade “matemática”,
mas sim pelo alcance atual do conhecimento.
Para obter êxito na leitura de A paixão medida, não basta ao programa buscar a relação
sonora de certas palavras que estão no texto com outras que não estão lá, considerando-
se que, no final das contas, o que se compreende do poema é como foram os caminhos,
a convergência e o clímax de uma relação amorosa, e tudo indica que para se buscar
esta compreensão é preciso ter interesse por ela. Para um programa, as tais palavras fora
do poema poderiam ser outras, justamente porque não haveria de sua parte, enquanto
programa, um interesse intrínseco e primário por aquela significação que parece
interessar à mente. Há alguns anos, o já mencionado cientista Marvin Minsky elaborou
uma conjetura segundo a qual uma atividade mental, como o pensamento, por exemplo,
seria semelhante e teria uma analogia profunda com uma orquestra executando uma
peça musical. Se isto um dia for verificado, a música ampliará de maneira significativa
o seu espaço como ciência uma vez que de sua compreensão dependerá a compreensão
de certos aspectos da mente. No caso do sonho, há um processo de síntese de imagens
como também a síntese de outras manifestações próprias dos demais sentidos, isto é,
sons, aromas, sensações térmicas, sabores, todas cooperando na configuração de uma
mensagem a ser decifrada. Decifrar este enigma significa buscar o autoconhecimento,
que é uma meta a ser atingida em escala social para o benefício de todos e de cada um e,
quem sabe, possa servir à construção de máquinas.
Em um futuro que se pareça um pouco com a ficção científica de hoje, é possível que a
computação, para continuar liderando a inteligência artificial, torne-se uma ciência
próxima das ciências humanas e sociais, isto porque precisará compreender a mente nas
suas galerias mais profundas e que vão além do domínio racional. Grande parte dos
fenômenos mentais mais complexos, e seus efeitos na vida da pessoa que os
experimenta, compreendendo-os parcialmente pela razão e processando-os na íntegra
pelos sentimentos, não é conhecida suficientemente pela ciência. A psicanálise —
assumida aqui como uma área de conhecimento importante na busca das últimas
fronteiras da mente — mostra-se vítima de seus próprios ensinamentos ao demonstrar
dificuldades de sair da sua infância científica e, assim, ainda não traz uma luz suficiente,
algo que explique formalmente, como convém à computação, o que são os complexos,
os medos, os desejos e tudo o mais que se manifesta nesse domínio chamado
inconsciente, se possível na mesma medida que a lógica o faz para o raciocínio. Há
alguns anos, difundiu-se nos meios acadêmicos que os seguidores de Freud estariam
levantando uma espécie de “gramática do inconsciente”, tratando-se, evidentemente, de
uma pesquisa que se vier um dia a ser bem sucedida trará um resultado que estará para a
inteligência artificial assim como estão a decifração dos hieróglifos para a história e o
reconhecimento da estrutura do DNA para a biologia. Entretanto, nada de concreto a
esse respeito consolidou-se a ponto de ser de utilidade para outras áreas de
conhecimento, especialmente para a computação.
É impossível dizer o que falta ou estimar o quanto falta em termos de tempo para que se
conheçam as últimas fronteiras da mente. O que não poderá ocorrer, no entanto, é mais
um hiato no desenvolvimento da computação — como aquele que separou o ábaco da
máquina de Pascal — porque desta vez há compromissos inadiáveis que o homem como
indivíduo assumiu com a sociedade, com a família e consigo próprio, assim como há
atitudes e posturas definitivas que o homem como espécie vem assumindo diante da
Natureza e que haverá de assumir diante do Universo, que o cerca e que o contém. Se
um dia a vida vier a sofrer descontinuidade severa a ponto de fazer o homem
desaparecer da face da Terra, não custa acreditar que as civilizações do futuro haverão
de reconhecer na humanidade as suas origens, justamente porque descobrirão que a
espécie humana conseguiu a proeza de equacionar-se a si própria e, por isto, teve os
instrumentos e o ímpeto de projetá-los algoritmicamente como cidadãos e cidadãs do
futuro distante.
IV. Fontes bibliográficas
1. ALLEN, J. The anatomy of Lisp. New York: McGraw-Hill, 1978.
2. BACKUS, J. “The syntax and semantics of the proposed international algebraic
language of the Zurich ACM-GAM Conference”. Proceedings of the International
Conference on Information Processing, Unesco, Paris, 1959, pp. 125-132.
3. CHURCH, A. “An unsolvable problem of elementary number theory”, American
Journal of Mathematics, Vol. 58, pp.345-363, 1936.
4. COHEN, P. “A teoria dos conjuntos e a hipótese do contínuo”. In Manuel Lourenço
(org.e trad.) O teorema de Gödel e a hipótese do contínuo. Lisboa: Fundação
Calouste Gulbenkian, 1979.
5. DIJKSTRA, E.W. A discipline of programming. Englewood Cliffs, New Jersey:
Prentice-Hall, 1976.
6. FONSECA FILHO, C. História da computação — teoria e tecnologia. São Paulo:
LTR Editora, 1999.
7. GÖDEL, K. “Sobre sentencias formalmente indecidibles de Principia Mathematica
y sistemas afines”. In Kurt Gödel, Obras completas (trad. Jesús Mosterín; original
de 1931), Madrid: Alianza Editorial, 1981.
8. GOSLING, J. et al. The Java language specification. Reading, Massachusetts:
Addison-Wesley , 1996.
9. HOARE, C.A.R. “An axiomatic basis for computer programming”.
Communications of the Association for the Computing Machinery, 12(10):576-583,
1969.
10. HOROWITZ, E. Fundamentals of programming languages. Rockville, Maryland:
Computer Science Press, 1983.
11. KAY, A. ‘The early history of Smalltalk”. ACM SIGPLAN, March 1993, pp.69-96.
12. KERNIGHAN, B.W. e RITCHIE, D. M. The C programming language. Englewood
Cliffs, New Jersey: Prentice-Hall, 1978.
13. KLEENE, S.C. “General recursive functions of natural numbers”. Mathematische
Annalen. Vol. 112, pp. 727-742, 1936.
14. KNUTH, D.E. The art of computer programming (3 volumes). Reading,
Massachusetts: Addison-Wesley, 1968.
15. LAMPORT, L. “Time, clocks, and the ordering of events in a distributed system”.
Communications of the ACM 21(7):558-565, 1978.
16. McCARTHY, J. “Recursive functions of symbolic expressions and their
computation by machine, part I”. Communications of the ACM, 3(4):184-195, 1960.
17. McCULLOCH, W. S. e PITTS, W. “A logical calculus of the ideas immanent in
nervous activity”. Bulletin of Mathematical Biophysics 5: 115-133, 1943.
18. MINSKY, M. “Music, mind, and meaning”. Computer Music Journal 5(3):28-44,
1981.
19. NAUR, P. et al. “Report on the algorithmic language Algol 60”. Communications of
the ACM, 3(5):299-314, 1960.
20. PETERSON, J.L. et al. Operating systems concepts. Reading, Massachusetts:
Addison-Wesley, 1985.
21. SHANK, R.C. e COLBY, K.M. (orgs.) Computer models of thought and language.
San Francisco: W.H. Ferman and Company, 1973.
22. SMULLYAN, R. Theory of formal systems. Princeton, New Jersey: Princeton
University Press, 1961.
23. TANENBAUM, A. S. Structured computer organization. Englewood Cliffs, New
Jersey: Prentice-Hall, 1984.
24. TURING, A. M. “On computable numbers with an application to the
Entscheidungsproblem”. Proceedings of the London Mathematical Society, vol. 42,
1936.
25. WEXELBLAT, R.L. History of programming languages. New York: Academic
Press, 1981.
26. WIRTH, N. “The programming language Pascal”. Acta informatica 1:35-63, 1971.