Linguagem Lisp Everton Luís Berz¹, Diogo Santinon¹ ¹ Faculdades de Informática de Taquara - Faculdades de Taquara (FACCAT) {everton, diogo}@faccat.br Resumo. Este artigo descreve um histórico, características e aplicabilidade da linguagem LISP (List Processing). No final do artigo é mostrado um exemplo de programa em LISP. 1. Histórico A linguagem LISP foi inspirada pelo Cálculo Lambda, um formalismo desenvolvido nos anos 30 por Alonzo Church. O Cálculo Lambda pode ser considerado como uma linguagem de programação abstrata, onde o conceito de Computação, isto é, as maneiras como funções podem ser combinadas para formar outras funções, aparecem em toda sua generalidade e de uma forma pura, sem complicações sintáticas. A principal característica do Cálculo Lambda é tratar funções como entidades que podem ser, como um dado qualquer, utilizadas como argumentos retornadas como valores de outras funções. Esta característica é preservada na linguagem LISP onde dados e funções tem exatamente o mesmo status. Podemos observar através disto que as m-expression (expressões-M), M significa Metalangue (meta linguagem) são também expressões lambda. São três os benefícios do Cálculo Lambda: Simplicidade: só existem três tipos de expressões (s-expressões, átomos e listas); Completo: não existe função que não possa ser representada; Generalidade: funções podem ser passadas como argumentos, simplificadas, comparadas, etc. O Cálculo Lambda é completo, mínimo e simples. Sua sintaxe é enxuta, contendo apenas três tipos de expressões, assim como sua semântica, que também é de fácil compreensão. A recursão é permitida livremente. Lisp é uma família de linguagens que possuem uma longa história; as primeiras idéias-chave para a linguagem foram desenvolvidas por John McCarthy em 1956, durante um projeto de pesquisa em inteligência artificial. A motivação de McCarthy surgiu da idéia de desenvolver uma linguagem algébrica para processamento de listas para trabalho em IA (inteligência artificial). Esforços para a implementação de seus primeiros dialetos foram empreendidos no IBM 704, IBM 7090, DEC PDP-1, DEC PDP-6 e DEC PDP-10. O dialeto principal entre 1960 e 1965 foi o Lisp 1.5. No início dos anos 70, houve outros dois dialetos predominantes, desenvolvidos através de esforços anteriores: MacLisp e Interlisp. O MacLisp aperfeiçoou a noção de variáveis especiais e o suporte a erros. Também introduziu o conceito com número variável de argumentos, macros, arrays, cálculos rápidos, além de enfatizar a velocidade de execução. O Interlisp introduziu muitas idéias na metodologia no ambiente de execução. Uma de suas idéias que influenciou os dialetos mais recentes foi à construção que mais tarde inspiraria a macro Loop, implementada por Warren Teitelman. Apesar das primeiras implementações do Lisp terem sido feitas nos IBM 704 e 7090, trabalhos posteriores concentravam-se nos DEC PDP-6 e PDP-10, este último sendo o baluarte do Lisp e das pesquisas em IA (inteligência artificial) em lugares como o MIT (Massachussets Institute of Tecnology) e as Universidades de Stanford e Carnegie-Mellon até metade dos anos 70. O computador PDP-10 e seu antecessor, o PDP-6 eram por definição, especialmente adequados para o Lisp, devido a possuírem palavras de 36 bits e endereços de 18 bits. Esta arquitetura permitia um registro de um conscell (par pontuado) em uma única palavra de memória, em instruções simples extraiam o seu car e cdr. Esses computadores possuíam também poderosas instruções de pilha, que proporcionavam rápida chamada às funções; porém suas limitações em 1973 eram evidentes: suportavam um pequeno número de pesquisadores utilizando o Lisp e seu endereçamento em 18 bits limitava o espaço dos programas. Uma resposta para o problema de endereçamento foi o desenvolvimento do "Lisp Machine", um computador dedicado especialmente à tarefa de trabalhar com a linguagem. Outra solução foi à utilização de computadores de uso geral com maior capacidade de endereçamento, como o DEC VAX e o S1 Mark IIA. Ao final dos anos 70, um grupo de pesquisadores do MIT (Massachussets Institute of Tecnology) deram início ao projeto NIL (New Implementation of Lisp) para o DEC VAX. Um dos êxitos do programa foi consertar muito dos já históricos, porém irritantes "bugs" que a linguagem apresentava. Simultaneamente, pesquisadores da Universidade de Stanford começaram o desenvolvimento de um dialeto específico para o supercomputador S1 Mark IIA, dialeto conhecido como S1 Lisp, que possuía a característica de não ser totalmente funcional. Esforços de padronização da linguagem iniciaram-se em 1969, quando Anthony Hearn e Martin Griss da Universidade de Utah definiram o Standard Lisp, um subconjunto do Lisp 1.5 e de outros dialetos. Nos anos seguintes, o mesmo grupo desenvolveu uma nova implementação conhecida como PSL (Portable Standard Lisp), que chegou a ser utilizado até meados da década de 80, devido a sua grande portabilidade. Durante a segunda metade dos anos 70, um importante avanço ocorreu: foi à implementação do Scheme Lisp, projetado por Gerald Sussman e Guy L. Steele Jr., o qual consiste em um dialeto relativamente simples, cujo projeto trouxe de volta alguns dos conceitos originais da semântica da linguagem. No final dos anos 70, a programação orientada a objetos trouxe a sua contribuição. Inspirados em alguns conceitos do Small Talk, pesquisadores do MIT (Massachussets Institute of Tecnology) desenvolveram o Flavors, primeiro Lisp com orientação a objetos. A própria Xerox também desenvolveu o seu dialeto Lisp: O LOOPS (Lisp Object-Oriented Programming System). A facilidade de utilização, adaptação e extensão da linguagem Lisp, aliada à sua origem acadêmica, fez com que surgissem dezenas de versões diferentes: FranzLisp, ZetaLisp, LeLisp, MacLisp, InterLisp, Scheme, T, Nil, XLisp, AutoLisp, etc, para nomear apenas as mais relevantes. Esta variedade de dialetos começou a tornar difícil a livre comunicação entre os membros da comunidade Lisp. Para obviar este problema, em Abril de 1981 os grupos de pesquisa Symbolics, NIL, S1 e SPICE reuniram-se para criar um Standard denominado Common Lisp com o objetivo de facilitar a troca de idéias (e programas). Citado por Antônio Menezes Leitão, “sendo a linguagem Common Lisp o herdeiro legítimo de todas as outras, ela deve suportar a maioria das capacidades que estas possuem. Como é lógico isto impede a estabilização da linguagem, que ainda não parou de evoluir, sendo ampliada de tempos a tempos para incorporar novos (e velhos) paradigmas de programação. Felizmente, essa ampliação evita (na medida em que isso é possível) alterar funcionalidades das versões anteriores e assim um programa Common Lisp tem a garantia de funcionar independentemente do estado atual da linguagem. O programa pode não estar tecnologicamente atual, mas estará sempre funcionalmente atual“. Citado por João Bosco da Mata Alves, “LISP tornou uma das principais linguagens de Inteligência Artificial”. 2. Paradigmas de Programação Principalmente devido ao seu longo tempo de existência, LISP é uma linguagem funcional atípica, pois também suporta muitas das estruturas das linguagens imperativas, estruturas essas ausentes nas modernas linguagens funcionais. 3. Características Técnicas A linguagem LISP é interpretada, onde o usuário digita expressões em uma linguagem formal definida e recebe de volta a avaliação de sua expressão. Deste ponto de vista podemos pensar no LISP como uma calculadora, que ao invés de avaliar expressões aritméticas avalia expressões simbólicas, chamadas de expressões. Cada programa em LISP, é, portanto, uma expressão. As expressões são de tamanho indefinido e tem uma estrutura de árvore binária. A estrutura de utilização da memória disponível é na forma de listas, pois livra o programador da necessidade de alocar espaços diferentes para o programa e para os dados, fazendo com que os dados e os programas sejam homogêneos, característica única da linguagem LISP. Suas principais características são: Tipos de dados: átomo e a lista. É com apenas esses dois tipos de dados que se constroem as expressões-S, as estruturas basilares de LISP. Fraca Tipagem: LISP, em relação a outras linguagens funcionais mais recentes, é fracamente tipado, o que causa complicações, já que operações que acessam as suas estruturas de dados são tratadas como funções. Funções de ordem elevada: Linguagens funcionais tipicamente suportam funções de ordem elevada (exemplo: função de uma função de uma função de uma...). Avaliação Ociosa: É o que ocorre quando uma função aninhada executa uma computação desnecessária para a avaliação da função que a chama, aumentando o tempo de execução. Concorrência (multitarefa): A concorrência nas linguagens imperativas tradicionais é relativamente complexa; o programador é o responsável pela sincronização de todas as tarefas (a multitarefa no paradigma procedural é tão sofisticada quanto um GOTO). Em contraste, as linguagens funcionais intrinsecamente nos oferecem oportunidades para a concorrência: A partir do momento em que uma função tem mais de um parâmetro, estes parâmetros devem em princípio ser avaliados simultaneamente (note que os parâmetros seriam as funções correspondentes às tarefas a serem executadas); A partir deste ponto, a responsabilidade pela sincronização das tarefas passa do programador para o compilador (as modernas linguagens funcionais orientadas a multitarefa dispõe de mecanismos através dos quais o programador pode guiar o compilador). Todavia, as linguagens funcionais orientadas a multitarefa permitem ao programador trabalhar em um nível muito mais elevado do que as linguagens imperativas destinadas a este mesmo fim. el de abstração, especialmente quando as funções são utilizadas, suprimindo muitos detalhes da programação e minimizando a probabilidade da ocorrência de muitas classes de erros; programas avaliações nas mais diferentes ordens. Esta característica de avaliação independente da ordem torna as linguagens funcionais as mais indicadas para a programação de computadores maciçamente paralelos; na os programas funcionais muito mais simples para provas e análises matemáticas do que os programas procedurais. E como desvantagem, destacamos: (ex. contas de banco) ou muitas atividades seqüenciais são muitas vezes mais fáceis de se trabalhar com programas procedurais ou programas orientados a objeto. O Common Lisp permite várias representações diferentes de números. Estas representações podem ser divididas em 4 tipos: hexadecimais, octais, binários e decimais. Estes últimos podem ser divididos em 4 categorias: inteiros, racionais, ponto flutuante e complexos. Funções em LISP: Chamada de função: (+ 3 2 5); =10 3.1 Avaliação Em Lisp, (+) é uma função, e uma expressão como (+ 2 3) é uma chamada de função envolvendo dois argumentos. Quando Lisp avalia uma chamada de função, ele o faz em duas etapas. Primeiramente os argumentos são avaliados, da esquerda para a direita. Neste contexto, cada argumento se "auto–avalia", e assim os valores dos argumentos são 2 e 3, respectivamente; finalmente, os valores dos argumentos são passados a função designada pelo operador. Neste caso, é a função +, que retorna 5. ; ==>Indica que é uma linha de comentário. (setf total 4) ==> seta a variável (total) com um valor (4); read – esta função não recebe nenhum argumento. O próprio comando não avalia sua entrada, apenas recebe os dados que o usuário digitar no teclado (sempre seguidos de <enter>). Geralmente vem combinado com outras funções: Ex: (setf total (read)) (define x 0) ==> seta x como 0; (display “Olá mundo”) ==> Escreve o conteúdo que esta dentro das aspas na saída padrão, o comando print é idêntico. (quote atomo1 atomo2 ... ) ==> Retorna um átomo (quando tem somente 1 argumento) ou uma lista a partir dos átomos. Esta função normalmente tem uma abreviação utilizando-se do apóstrofe. (quote jose silva) <==> '(jose silva) (car lista) ==> Retorna o primeiro elemento da lista. Ex: (car '(jose silva)) retorna jose. Nas implementações mais recentes o LISP foi padronizado pela ISO e esta função foi duplicada como nome de first. (cdr lista) ==> Retorna a lista sem o primeiro elemento. Ex: (cdr '(jose da silva)) retorna (da silva). Novo nome desta função rest. (cons atomo lista) ==> Adiciona átomo ao início da lista. Ex: (cons 'jose '(da silva)) retorna (jose da silva). Funções matemáticas + --> Adição - --> Subtração * --> Multiplicação / --> Divisão ( sqrt 4 ) 2.0 ==> função q retorna o quadrado no número ( expt 2 10 ) 1024 ==> função exponencial 4. Aplicabilidade da LP Lisp é uma linguagem madura, concebida atenciosamente, altamente portável, linguagem de força industrial na qual desenvolvedores sérios em todo o mundo contam para: ara fazer coisas do dia a dia. outra linguagem. elas que necessitam de mudanças após a etapa inicial. A linguagem teve um grande sucesso em software do ramo de negócios, engenharia, processamento de documentos, hipermídia (incluindo a Web), matemática, gráficos e animação, inteligência artificial e processamento de linguagem natural. Uma das grandes vantagens de Lisp é que ela trata o programa como dado, possibilitando assim um programa inteiro ser dado como entrada de um outro, coisa que não acontece em outras linguagens como C e Pascal. E usada algumas vezes para definir todos os aspectos de uma aplicação, ou apenas o motor de processamento interno, ou apenas a interface do usuário; e ainda é usada com rotina para prover linguagens de comando interativas, linguagens de macro ou script e linguagens extensoras de sistemas comerciais. 5. Exemplo de Programa ;Programa de calculo de media das notas e situação final de alunos da FACCAT ; (http://www.faccat.br) ; Pode ser baixado neste endereco: http://fit.faccat.br/~everton/lp/media.lsp ; Para rodar digite no console: clisp media.lsp ; ;** Funcao de validacao da nota ******************************************************************* (defun validanota (nota) (setf NOTAMAXIMA 10) (setf NOTAMINIMA 0) ; (setf retorno T) ; (if (or (> nota NOTAMAXIMA) (< nota NOTAMINIMA)) (progn (princ "Nota inválida. A nota deve estar entre ") (princ NOTAMINIMA) (princ " e ") (princ NOTAMAXIMA) (terpri) (setf retorno nil) ) ) retorno ) ;** Inicio ******************************************************************** (setf MEDIASEMESTREAPROVACAO 8) (setf MEDIAFINALAPROVACAO 6) ; (setf g1 -1) (setf g2 -1) (setf sg1 -1) (setf sg2 -1) (setf ms -1) (setf ex -1) (setf mf -1) (setf fazerSG 'sim) (setf qualSG 0) (setf fazerEX 'sim) ; (terpri) (princ "Aplicação iniciada...") (terpri) (terpri) (princ "Sistema de cálculo de média FACCAT") (terpri) (terpri) (terpri) ;******************** G1 (loop (progn (princ "Entre com a nota do G1: ") (setf g1 (float (read))) (if (validanota g1) (return) ) ) ) (terpri) ;******************** G2 (loop (progn (princ "Entre com a nota do G2: ") (setf g2 (float (read))) (if (validanota g2) (return) ) ) ) (terpri) ;************************ EXIBE MEDIA DO SEMESTRE (setf ms (/ (+ g1 g2) 2)) (princ "Média do Semestre: ") (princ ms) (terpri) ;******************** SG (if (>= (/ (+ g1 g2) 2) MEDIASEMESTREAPROVACAO) (progn (princ "Voce ja está aprovado. Deseja fazer substituição mesmo assim (sim|nao)? ") (setf fazerSG (read)) (terpri) ) ) ; (if (equal fazerSG 'sim) (progn (princ "Qual substituição deseja fazer (1|2|nenhum) ? ") (setf qualSG (read)) (terpri) ;******************** SG1 (if (equal qualSG 1) (loop (progn (princ "Entre com a nota da subst. do G1: ") (setf sg1 (float (read))) (if (validanota sg1) (return) ) ) ) ) ;******************** SG2 (if (equal qualSG 2) (loop (progn (princ "Entre com a nota da subst. do G2: ") (setf sg2 (float (read))) (if (validanota sg2) (return) ) ) ) ) ) ) ;************************ EXIBE MEDIA DO SEMESTRE (if (= sg1 -1) (setf sg1 g1) ) (if (= sg2 -1) (setf sg2 g2) ) ;deve ficar a nota mais alta (if (< sg1 g1) (setf sg1 g1) ) (if (< sg2 g2) (setf sg2 g2) ) ; (setf ms (/ (+ sg1 sg2) 2)) (princ "Média do Semestre: ") (princ ms) (terpri) (terpri) ;************************ EXAME (if (>= (/ (+ sg1 sg2) 2) MEDIASEMESTREAPROVACAO) (progn (princ "Voce ja está aprovado. Deseja fazer exame mesmo assim (sim|nao)? ") (setf fazerEX (read)) (terpri) ) ) ; (if (equal fazerEX 'sim) (loop (progn (princ "Entre com a nota do exame: ") (setf ex (float (read))) (if (validanota ex) (return) ) ) ) ) ;************************ CALCULA MEDIA FINAL (if (= ex -1) (setf mf ms) (setf mf (/ (+ ms ex) 2)) ) (princ "Média final: ") (princ mf) (terpri) ;************************ EXIBE RESULTADO (princ "Situação: ") (if (>= mf MEDIAFINALAPROVACAO) (princ "Aprovado") (princ "Reprovado") ) (terpri) (terpri) ; (princ "Sistema encerrado.") (terpri) 6. Bibliografia http://pt.wikipedia.org/wiki/Lisp http://www.dca.fee.unicamp.br/courses/EA072/lisp9596/Lisp9596.html http://www.cadblocos.arq.br/lisp.html http://www.gia.ist.utl.pt/Lisp9596/Lisp9596.html http://edsonjr.prof.unopar.br/cad_menu/lisp.html http://www.portaldaprogramacao.com/index_linguagem.asp?c=50 http://minerva.ufpel.edu.br/~marhinz/avaliador.htm Software CLEAN esta disponivel via ftp: ftp://ftp.cs.kun.nl/pub/Clean http://www.milenio.com.br/grottoli/Academicos/introducao_lisp.zip