LISP é um conceito de programação funcional especificado em 1958 por John McCarthy, que deu origem a uma família de linguagens. É baseado no cálculo-λ, sistema formal de cálculo baseado em funções criado por Alonzo Church na década de 1930, e foi projetado para lidar com dados simbólicos, em oposição a dados numéricos, o padrão da maioria das linguagens imperativas.
LISP significa list processing, processamento de listas, e lista é a estrutura fundamental da linguagem. Todos os dados, assim como o código em si, são representados como listas.
Por exemplo, a soma de 1 e 2 é escrita como:
(+ 1 2)
Que é lido como a lista dos elementos +
, 1
e 2
. O processamento da lista se dá separando o primeiro elemento (head ou
car
) dos demais (tail ou cdr
):
(+ . (1 2))
[update 2017-11-20]
Os termos
car
ecdr
são comandos do IBM 704, para o qual LISP foi desenvolvido. Significam Contents of the Address part of Register number e Contents of the Decrement part of Register number.[/update]
O primeiro elemento representa uma função λ e os demais elementos representam os parâmetros para esta função. No exemplo a função é '+
, que
retorna a soma dos parâmetros, e os parâmetros são 1 e 2.
As implementações / dialetos mais importantes de LISP são Common Lisp, Emacs Lisp, Scheme e Clojure.
Todos os dialetos de LISP seguem a mesma construção (funcional e processamento de listas), mas com variações nos nomes das funções e nas abordagens.
Por exemplo, fatorial simples.
Em Common Lisp:
(defun factorial (n)
(if (= n 0)
1
(* n (factorial (- n 1)))))
Em Scheme R5RS:
(define factorial
(lambda (n)
(if (zero? n)
1
(* n (factorial (- n 1))))))
Em Clojure, a implementação recursiva do fatorial estoura a pilha da JVM, então é preciso usar abordagens mais compatíveis com o funcionamento dela, como um loop:
(defn factorial [x]
(loop [n x f 1]
(if (= n 1)
f
(recur (dec n) (* f n)))))
Ou map/reduce:
(defn factorial [n]
(reduce * (range 1 (inc n))))
Scheme por sua vez possui algumas implementações / variantes importantes: Guile, MIT/GNU Scheme e Racket.
Racket foi inicialmente chamado PLT-Scheme, depois renomeado para Racket. É uma plataforma RAD completa, com IDE (DrRacket), gerador de interface WYSIWYG (MrEd Designer) e interpretador.
Racket, enquanto plataforma de desenvolvimento, suporta uma série de liguagens, entre elas R5RS, R6RS, Racket, uma versão lazy de Racket e até mesmo C.
Por exemplo, a implementação de fatorial acima pode ser implementada em Racket da seguinte forma:
#!racket
(define factorial
(λ (n)
[if (zero? n)
1
(* n (factorial (- n 1)))]))
A primeira linha indica qual a linguagem usada. Outras possibilidades poderiam ser #!r5rs
para R5RS, #!r6rs
para R6RS ou
#!lazy
para Lazy Racket. Para C, o hash bang fica um pouco
mais complexo:
#!planet jaymccarthy/c
A instrução 'define
define o valor de um slot, no caso 'factorial
, que armazena o resultado da lista no próximo parâmetro.
A instrução 'λ
cria uma função λ. O primeiro parâmetro é uma lista dos argumentos, o segundo parâmetro é o corpo da função.
No caso a função é o resultado da instrução 'if
: o primeiro parâmetro é a condição, o segundo o resultado em caso verdadeiro e o terceiro o
resultado em caso falso.
zero?
retorna verdadeiro (#t
) se o parâmetro for zero, ou falso (#f
) se não for. O restante ficou fácil de entender agora.
No próximo artigo darei um exemplo de Lazy Racket, que usa promises para resolver algumas operações.
Concept | Functional | LISP