Lispでリフレクション第01回
Lispで、Javaのリフレクションのようなことをやりたい。具体的には、(動的な)変数から呼び出したいメソッド名を
作成して、その変数名のメソッドをそのまま実行する、というようなイメージ。そのために以下のようなコードを
書いて試してみた。
(defun method-1 () (format t "This is a method-1.~%")) (defmacro invoke-method (param &rest args) (let ((method-name (append-string "method-" (string param)))) `(,(intern method-name) ,@args))) (invoke-method "1")
予想では、最後の (invoke-method "1") の結果、method-1 が呼び出されるはず、と思っていたが、実際には以下のような感じでエラーになった。
> Error: Undefined function |method-1| called with arguments () . > While executing: FUNCALL, in process listener(1). > Type :GO to continue, :POP to abort, :R for a list of available restarts. > If continued: Retry applying |method-1| to NIL. > Type :? for other options. 1 >
余計なものがくっついている。なぜ前後に | があるのか・・・調べてみると、これは大文字小文字の区別があったり、
あるいは空白(スペース)を含むシンボルだと自動で付与されるもののようだ。実際、
- method-1 の名前を |method-1| にする
- 作成後のメソッド名変数を大文字に変換する
のいずれかを実施すれば、期待通りに動くことを確認。
最終的には以下のようになった。
(defun method-1 () (format t "This is a method-1.~%")) (defun method-2 () (format t "This is a method-2.~%")) (defun method-3 () (format t "This is a method-3.~%")) (defmacro invoke-method (param &rest args) (let ((method-name (string-upcase (append-string "method-" (string param))))) `(,(intern method-name) ,@args))) (invoke-method "1") (invoke-method "2") (invoke-method "3")
実行結果は以下のとおり。
This is a method-1. This is a method-2. This is a method-3.
intern は、オプションでパッケージ名を渡して起動することができる。渡さない場合、カレントの
パッケージ名が使用される。全てカレントのパッケージで実行されるのなら問題ないかもしれないが、
パッケージを意識した場合は、少し手を加える必要があるかもしれない。