Para dar uma resposta curta, as macros são usadas para definir extensões de sintaxe de idioma para Common Lisp ou Linguagens Específicas de Domínio (DSLs). Essas linguagens são incorporadas diretamente no código Lisp existente. Agora, as DSLs podem ter sintaxe semelhante ao Lisp (como o Prolog Interpreter para Common Lisp de Peter Norvig ) ou completamente diferente (por exemplo, Infix Notation Math for Clojure).
Aqui está um exemplo mais concreto:
Python possui compreensão de lista incorporada à linguagem. Isso fornece uma sintaxe simples para um caso comum. A linha
divisibleByTwo = [x for x in range(10) if x % 2 == 0]
gera uma lista contendo todos os números pares entre 0 e 9. De volta aos 1,5 dias do Python, não havia essa sintaxe; você usaria algo mais como este:
divisibleByTwo = []
for x in range( 10 ):
if x % 2 == 0:
divisibleByTwo.append( x )
Ambos são funcionalmente equivalentes. Vamos invocar nossa suspensão de descrença e fingir que o Lisp tem uma macro de loop muito limitada que apenas faz iteração e não é uma maneira fácil de fazer o equivalente à compreensão da lista.
No Lisp, você pode escrever o seguinte. Devo observar que este exemplo artificial é escolhido para ser idêntico ao código Python, não um bom exemplo de código Lisp.
;; the following two functions just make equivalent of Python's range function
;; you can safely ignore them unless you are running this code
(defun range-helper (x)
(if (= x 0)
(list x)
(cons x (range-helper (- x 1)))))
(defun range (x)
(reverse (range-helper (- x 1))))
;; equivalent to the python example:
;; define a variable
(defvar divisibleByTwo nil)
;; loop from 0 upto and including 9
(loop for x in (range 10)
;; test for divisibility by two
if (= (mod x 2) 0)
;; append to the list
do (setq divisibleByTwo (append divisibleByTwo (list x))))
Antes de ir mais longe, devo explicar melhor o que é uma macro. É uma transformação realizada em código por código. Ou seja, um pedaço de código, lido pelo intérprete (ou compilador), que recebe o código como argumento, manipula e retorna o resultado, que é executado no local.
É claro que muitas digitações e programadores são preguiçosos. Assim, poderíamos definir o DSL para fazer a compreensão da lista. De fato, já estamos usando uma macro (a macro de loop).
Lisp define algumas formas especiais de sintaxe. A citação ( '
) indica que o próximo token é literal. O quasiquote ou backtick ( `
) indica que o próximo token é um literal com escapes. Escapes são indicados pelo operador de vírgula. O literal '(1 2 3)
é o equivalente ao Python [1, 2, 3]
. Você pode atribuí-lo a outra variável ou usá-lo no local. Você pode pensar `(1 2 ,x)
como o equivalente do Python, [1, 2, x]
onde x
é uma variável definida anteriormente. Essa notação de lista faz parte da mágica que entra nas macros. A segunda parte é o leitor Lisp, que substitui inteligentemente as macros por código, mas que é melhor ilustrado abaixo:
Assim, podemos definir uma macro chamada lcomp
(abreviação de compreensão de lista). Sua sintaxe será exatamente como o python que usamos no exemplo [x for x in range(10) if x % 2 == 0]
-(lcomp x for x in (range 10) if (= (% x 2) 0))
(defmacro lcomp (expression for var in list conditional conditional-test)
;; create a unique variable name for the result
(let ((result (gensym)))
;; the arguments are really code so we can substitute them
;; store nil in the unique variable name generated above
`(let ((,result nil))
;; var is a variable name
;; list is the list literal we are suppose to iterate over
(loop for ,var in ,list
;; conditional is if or unless
;; conditional-test is (= (mod x 2) 0) in our examples
,conditional ,conditional-test
;; and this is the action from the earlier lisp example
;; result = result + [x] in python
do (setq ,result (append ,result (list ,expression))))
;; return the result
,result)))
Agora podemos executar na linha de comando:
CL-USER> (lcomp x for x in (range 10) if (= (mod x 2) 0))
(0 2 4 6 8)
Bem arrumado, não é? Agora não para por aí. Você tem um mecanismo ou um pincel, se quiser. Você pode ter qualquer sintaxe que desejar. Como Python ou with
sintaxe do C # . Ou sintaxe LINQ do .NET. No final, é isso que atrai as pessoas para o Lisp - a máxima flexibilidade.