Kodumaro :: LISP

Publicado em 2 de Novembro, 2017
As sombras da programação.
Made with secret alien technology

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 e cdr 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

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.

Continua…

No próximo artigo darei um exemplo de Lazy Racket, que usa promises para resolver algumas operações.

Conceitual | Funcional | LISP