Ads 468x60px

quinta-feira, 23 de fevereiro de 2012

Programação funcional: Lisp e dialetos

Programação funcional é um tópico muito relevante aos interessados em ciência da computação, sendo a família de linguagens Lisp muito utilizada em cursos introdutórios e pesquisas em inteligência artificial. Confira aqui as principais ideias, conceitos, aplicações e motivações para explorar Lisp. Em especial, veremos também uma introdução básica a Scheme.


O que é Lisp?


Lisp (acrônimo de LISt Processing) é uma família de linguagens com histórico datando de 1958, inventada por John McCarthy. Claro, as versões que usamos hoje são bem diferentes da de 1958. Com o passar do tempo, vários dialetos foram surgindo para se adequarem a situações distintas. Hoje em dia, alguns dos principais são: Common Lisp, Scheme, Clojure e Emac Lisp.

Suas principais aplicações são tanto no âmbito acadêmico, servindo como uma ótima linguagem de programação para iniciantes e ferramenta para estudar conceitos de ciência da computação, quanto em aplicações envolvendo inteligência artificial. Isso se deve à peculiar sintaxe e poderosa capacidade de abstração, que demonstraremos na prática daqui a pouco.

Mas também há espaço para Lisp em aplicações diversas. Viaweb, depois renomeado Yahoo! Store, foi um popular serviço de montagem e hospedagem de lojas virtuais escrito primariamente em Lisp. Vale também citar o AutoLISP, dialeto de Lisp usado em AutoCAD e derivados para escrever macros sofisticados.

Paradigma de programação: Programação funcional


As linguagens de programação funcionais, como Lisp, tem como filosofia máxima descrever "o quê" fazer através de funções matemáticas, e não "como" fazer. Linguagens imperativas (C, C++, Java, Python...), em contraste, enfatizam as mudanças de estado que compõem a computação e por consequência costumam ter um processamento mais eficiente, porém com menor poder de abstração.

Relacionado a essa característica está um dos principais motivos para aprender Lisp: expandir seu modo de pensar, descobrir novas formas de solucionar problemas. Eric Raymond, autor de "Como se tornar um hacker", cita que aprender Lisp é uma experiência enriquecedora, tornando-o um programador melhor mesmo que você nunca use muito a linguagem no dia-a-dia.

Tutorial prático: Scheme em 5 minutos


Como uma pequena amostra de Lisp, vamos adotar o dialeto Scheme para demonstrar algumas de suas funcionalidades. E quando se fala em Scheme, automaticamente vem o nome DrRacket (antigo DrScheme). DrRacket é um software gratuito que serve como ambiente de programação ideal a iniciantes, por implementar diversos pacotes de utilidade (por exemplo, pacotes gráficos) e oferecer diferentes níveis de gramática. O iniciante tem acesso a um número limitado de funcionalidades, expandidas conforme ele avança.

Primeiro, baixe e instale a última versão do Racket através do site oficial. A interface do programa deve ser parecida com esta:

Interface do software DrRacket

Há uma janela de definições, onde fica o código-fonte do seu programa, e uma janela de interações, um pequeno interpretador dinâmico. Hora de brincar um pouco na janela de interações, começando por algumas operações aritméticas:

(+ 1 1) -> resultado: 2.
(- 1 1) -> resultado: 0.
(* 1 1) -> resultado: 1.
(/ 1 0) -> resultado:  /: divide by zero.
(sqrt 4) -> resultado: 2.
(sqrt -4) -> resultado: 0+2i.
(+ (+ 1 1) (+ 2 2)) -> resultado: 6.

A primeira coisa que chama a atenção são os parênteses, característica comum aos dialetos de Lisp. Não é à toa que ela às vezes é chamada Lisp - Lots of Irritating Stupid Parentheses, mas são justamente esses parênteses irritantes que ressaltam as vantagens da linguagem. Vejamos como definir nossas próprias funções por meio do comando define:

(define (foo a b)
  (* a b)
)


Podemos testá-la com valores quaisquer, por exemplo 1 e 2: (foo 1 2), retornando 2 como resultado. Opa, mas isso é o mesmo que (* 1 2), e a sintaxe também é semelhante. Não é difícil deduzir que Scheme trata as operações aritméticas como funções, do mesmo tipo que o nosso foo.

Você também deve ter percebido que a função foo recebe dois argumentos, mas não especifica seu tipo (tipagem dinâmica). É perfeitamente possível declarar (foo "oi" "oi"), mas o resultado será um erro lógico devido ao fato de que a multiplicação só aceita números. Por sorte, há um jeito de contornar isso: predicados e tratamento de erros.

(define (foo a b)
  (cond
    [(and (number? a) (number? b)) (* a b)]
    [else (error 'foo "Não são números!")]
  )
)


Se testarmos novamente (foo 1 2), o resultado continuará sendo 3, mas agora (foo "oi" "oi") nos retorna "foo: Não são números!". Isso não é muito parecido com o erro obtido pela divisão por zero, "/: divide by zero"? Como dito, operações aritméticas nada mais são do que funções, então faz sentido que os erros sejam tratados da mesma maneira.

Essa nova função nos permite falar sobre alguns tópicos interessantes em Scheme:

  • Estrutura de decisão: A operação cond atua da mesma forma que um if-else em linguagens imperativas. Cada condição é encapsulada por chaves, [], e é composta por uma expressão booleana e uma ação. A palavra-chave else representa qualquer outra condição não especificada de forma explícita.
  • Expressões booleanas: Scheme avalia expressões como (= a b), (< a b) e (> a b), retornando um indicador de verdadeiro ou falso. Expressões complexas podem ser formadas pelos operadores and, or e not. Assim, (and (= 1 1) (= 2 3)) é falso pois 2 é diferente de 3, mas (or (= 1 1) (= 2 3)) é verdadeiro pois pelo menos uma expressão é verdadeira.
  • Predicados: Embora não os declaremos explicitamente, Scheme é capaz de avaliar os tipos dos dados que estamos processando. Os predicados são expressões booleanas da forma (tipo? var), onde o tipo pode ser number, boolean, symbol etc., e var é uma variável ou constante qualquer.
  • Erros: A operação error é responsável por avisar ao usuário de uma operação inválida e parar o processamento do programa imediatamente.

Quer aprender mais? Então dê uma olhada no livro Structure and Interpretations of Computer Programs, uma ótima introdução à programação e ciência da computação, utilizando a linguagem Scheme. O livro está disponível online, mas também há a possibilidade de uma cópia física:


Fontes


Beating the Averages
How to Design Programs
Lisp (programming language) - Wikipedia
Structure and Interpretation of Computer Programs - MIT OpenCourseWare

0 comentários:

Postar um comentário