Щоб дати коротку відповідь, макроси використовуються для визначення розширень синтаксису мови до загальних мов Lisp або домену (DSL). Ці мови вбудовані прямо у існуючий код Lisp. Тепер DSL можуть мати синтаксис, схожий на Lisp (наприклад, перекладач прологів Peter Norvig для звичайного Lisp) або зовсім інший (наприклад, Infix Notation Math for 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, а не хороший приклад коду 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))))
Перш ніж піти далі, я повинен краще пояснити, що таке макрос. Це перетворення, яке здійснюється кодом за кодом. Тобто фрагмент коду, прочитаний інтерпретатором (або компілятором), який приймає код як аргумент, маніпулює та повертає результат, який потім запускається на місці.
Звичайно, це дуже багато набору тексту, і програмісти лінуються. Таким чином, ми могли б визначити DSL для розуміння списку. Насправді ми вже використовуємо один макрос (цикл макросу).
Лісп визначає пару спеціальних синтаксичних форм. Цитата ( '
) вказує, що наступний жетон є буквальним. Квазіквіт або backtick ( `
) вказує, що наступний жетон - буквальний з утечами. Ескапелі вказуються оператором комами. Буквал '(1 2 3)
- еквівалент Python [1, 2, 3]
. Ви можете призначити її іншій змінній або використовувати її на місці. Ви можете вважати `(1 2 ,x)
еквівалентом Python, [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 # . Або синтаксис LINQ .NET. Врешті-решт, саме це приваблює людей до Ліспа - абсолютна гнучкість.