Ein Dekorateur ist im Grunde nur eine Funktion .
Beispiel in Common Lisp:
(defun attributes (keywords function)
(loop for (key value) in keywords
do (setf (get function key) value))
function)
Oben ist die Funktion ein Symbol (das von zurückgegeben würde DEFUN) und wir fügen die Attribute in die Eigenschaftsliste des Symbols ein .
Jetzt können wir es um eine Funktionsdefinition schreiben:
(attributes
'((version-added "2.2")
(author "Rainer Joswig"))
(defun foo (a b)
(+ a b))
)
Wenn wir eine ausgefallene Syntax wie in Python hinzufügen möchten, schreiben wir ein Reader-Makro . Mit einem Reader-Makro können wir auf der Ebene der S-Ausdruckssyntax programmieren:
(set-macro-character
#\@
(lambda (stream char)
(let ((decorator (read stream))
(arg (read stream))
(form (read stream)))
`(,decorator ,arg ,form))))
Wir können dann schreiben:
@attributes'((version-added "2.2")
(author "Rainer Joswig"))
(defun foo (a b)
(+ a b))
Der Lisp- Leser liest oben:
(ATTRIBUTES (QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(DEFUN FOO (A B) (+ A B)))
Jetzt haben wir eine Form von Dekorateuren in Common Lisp.
Makros und Reader-Makros kombinieren.
Eigentlich würde ich oben die Übersetzung in echtem Code mit einem Makro machen, nicht mit einer Funktion.
(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))))
Die Verwendung erfolgt wie oben mit demselben Reader-Makro. Der Vorteil ist , dass der Lisp - Compiler sieht es immer noch als sogenannte Top-Level - Form - die * Datei Compiler behandelt Top-Level - Formulare speziell zum Beispiel fügt sie Informationen über sie in die Kompilierung- Umgebung . Im obigen Beispiel können wir sehen, dass das Makro in den Quellcode schaut und den Namen extrahiert.
Der Lisp- Leser liest das obige Beispiel in:
(DEFDECORATOR ATTRIBUTES
(QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(DEFUN FOO (A B) (+ A B)))
Welches wird dann Makro erweitert in:
(PROGN (DEFUN FOO (A B) (+ A B))
(ATTRIBUTES (QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(QUOTE FOO)))
Makros unterscheiden sich stark von Reader-Makros .
Makros bekommen den Quellcode übergeben, können machen was sie wollen und dann den Quellcode zurückgeben. Die Eingabequelle muss kein gültiger Lisp-Code sein. Es kann alles sein und es könnte ganz anders geschrieben sein. Das Ergebnis muss dann ein gültiger Lisp-Code sein. Wenn der generierte Code jedoch auch ein Makro verwendet, könnte die Syntax des in den Makroaufruf eingebetteten Codes wiederum eine andere sein. Ein einfaches Beispiel: Man könnte ein mathematisches Makro schreiben, das eine Art mathematische Syntax akzeptiert:
(math y = 3 x ^ 2 - 4 x + 3)
Der Ausdruck y = 3 x ^ 2 - 4 x + 3ist kein gültiger Lisp-Code, aber das Makro könnte ihn zum Beispiel analysieren und einen gültigen Lisp-Code wie diesen zurückgeben:
(setq y (+ (* 3 (expt x 2))
(- (* 4 x))
3))
Es gibt viele andere Anwendungsfälle von Makros in Lisp.