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 + 3
ist 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.