デコレータは、基本的にはある機能。
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-expression構文のレベルでプログラミングできます。
(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コンパイラーがまだそれをいわゆるトップレベルフォームと見なしていることです。* fileコンパイラーはトップレベルフォームを特別に扱います。たとえば、それらの情報をコンパイル時環境に追加します。上記の例では、マクロがソースコードを調べて名前を抽出していることがわかります。
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))
Lispにはマクロの他の多くのユースケースがあります。