Чтобы дать краткий ответ, макросы используются для определения языковых синтаксических расширений для Common Lisp или Domain-Specific Languages (DSL). Эти языки встроены прямо в существующий код на Лиспе. Теперь DSL могут иметь синтаксис, подобный Lisp (например, интерпретатор Prolog Питера Норвига для Common Lisp) или совершенно другой (например, Infix Notation Math для Clojure).
Вот более конкретный пример:
Python имеет встроенные в язык списки. Это дает простой синтаксис для общего случая. Линия
divisibleByTwo = [x for x in range(10) if x % 2 == 0]
выдает список, содержащий все четные числа от 0 до 9. В Python 1,5 дня не было такого синтаксиса; вы бы использовали что-то вроде этого:
divisibleByTwo = []
for x in range( 10 ):
if x % 2 == 0:
divisibleByTwo.append( x )
Оба они функционально эквивалентны. Давайте вызовем нашу приостановку неверия и притворимся, что у Лиспа очень ограниченный макрос цикла, который просто выполняет итерации, и нет простого способа сделать эквивалентную работу со списком.
В Лиспе вы могли бы написать следующее. Я должен отметить, что этот надуманный пример подобран к коду Python, а не к хорошему примеру кода на Лиспе.
;; 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))))
Прежде чем идти дальше, я должен лучше объяснить, что такое макрос. Это преобразование, выполняемое для кода за кодом. То есть фрагмент кода, читаемый интерпретатором (или компилятором), который принимает код в качестве аргумента, манипулирует и возвращает результат, который затем запускается на месте.
Конечно, это много печатать, и программисты ленивы. Таким образом, мы могли бы определить DSL для выполнения списка. Фактически, мы уже используем один макрос (макрос цикла).
Лисп определяет пару специальных синтаксических форм. Кавычка ( '
) указывает, что следующий токен является литералом. Квазицитат или backtick ( `
) указывает, что следующий токен является литералом с escape-символами . Побеги указаны оператором запятой. Литерал '(1 2 3)
является эквивалентом Python [1, 2, 3]
. Вы можете назначить его другой переменной или использовать на месте. Вы можете думать `(1 2 ,x)
как эквивалент Python's, [1, 2, x]
где x
переменная определена ранее. Эта запись списка является частью магии, которая входит в макросы. Вторая часть - читатель Lisp, который разумно заменяет макросы на код, но это лучше всего показано ниже:
Таким образом, мы можем определить макрос с именем lcomp
(сокращение от понимания списка). Его синтаксис будет точно таким же, как у питона, который мы использовали в примере [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)))
Теперь мы можем выполнить в командной строке:
CL-USER> (lcomp x for x in (range 10) if (= (mod x 2) 0))
(0 2 4 6 8)
Довольно аккуратно, а? Теперь это не останавливается там. У вас есть механизм или кисть, если хотите. Вы можете иметь любой синтаксис, какой только захотите. Как и with
синтаксис Python или C # . Или .NET синтаксис LINQ. В конце концов, это то, что привлекает людей в Lisp - максимальная гибкость.