Декоратор в основном только функция .
Пример в Common Lisp:
(defun attributes (keywords function)
(loop for (key value) in keywords
do (setf (get function key) value))
function)
Выше функция является символом (который будет возвращен DEFUN
), и мы помещаем атрибуты в список свойств символа .
Теперь мы можем написать это вокруг определения функции:
(attributes
'((version-added "2.2")
(author "Rainer Joswig"))
(defun foo (a b)
(+ a b))
)
Если мы хотим добавить необычный синтаксис, как в Python, мы напишем макрос для чтения . Макрос читателя позволяет нам программировать на уровне синтаксиса s-выражения:
(set-macro-character
#\@
(lambda (stream char)
(let ((decorator (read stream))
(arg (read stream))
(form (read stream)))
`(,decorator ,arg ,form))))
Затем мы можем написать:
@attributes'((version-added "2.2")
(author "Rainer Joswig"))
(defun foo (a b)
(+ a b))
Читатель Lisp читает выше, чтобы:
(ATTRIBUTES (QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(DEFUN FOO (A B) (+ A B)))
Теперь у нас есть форма декораторов в Common Lisp.
Сочетание макросов и читательских макросов.
На самом деле я бы сделал перевод выше в реальном коде, используя макрос, а не функцию.
(defmacro defdecorator (decorator arg form)
`(progn
,form
(,decorator ,arg ',(second form))))
(set-macro-character
#\@
(lambda (stream char)
(declare (ignore char))
(let* ((decorator (read stream))
(arg (read stream))
(form (read stream)))
`(defdecorator ,decorator ,arg ,form))))
Использование, как указано выше, с тем же макросом читателя. Преимущество заключается в том, что компилятор Lisp по-прежнему рассматривает ее как так называемую форму верхнего уровня - файловый компилятор * обрабатывает формы верхнего уровня специально, например, добавляет информацию о них в среду времени компиляции . В приведенном выше примере мы видим, что макрос просматривает исходный код и извлекает имя.
Читатель Lisp читает приведенный выше пример в:
(DEFDECORATOR ATTRIBUTES
(QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(DEFUN FOO (A B) (+ A B)))
Который затем получает макрос, расширенный в:
(PROGN (DEFUN FOO (A B) (+ A B))
(ATTRIBUTES (QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(QUOTE FOO)))
Макросы очень отличаются от макросов читателя .
Макросы передают исходный код, могут делать что хотят, а затем возвращают исходный код. Входной источник не должен быть действительным кодом Lisp. Это может быть что угодно, и это может быть написано совершенно по-другому. Результат должен быть действительным кодом Lisp тогда. Но если сгенерированный код также использует макрос, то синтаксис кода, встроенного в вызов макроса, снова может быть другим синтаксисом. Простой пример: можно написать математический макрос, который будет принимать некоторый математический синтаксис:
(math y = 3 x ^ 2 - 4 x + 3)
Выражение y = 3 x ^ 2 - 4 x + 3
не является допустимым кодом Lisp, но макрос может, например, проанализировать его и вернуть действительный код Lisp следующим образом:
(setq y (+ (* 3 (expt x 2))
(- (* 4 x))
3))
Есть много других случаев использования макросов в Лиспе.