文字列と同名の関数を呼出す
Java のリフレクションのようなことをやりたい。具体的には、文字列と同名の関数を呼出したい。
例えば、文字変数(String Symbol?)の値が hello のとき、関数 hello を呼出す、ということをしたい。
* (defun hello () (format t "Hello!~%")) HELLO * (funcall #'hello) Hello! NIL *
まあ、ここまでは普通。この "#'hello" の部分を変数から生成したい。どうすればいいか?
* (intern "HELLO") HELLO :INTERNAL * (funcall #'(intern "HELLO")) ; in: FUNCALL #'(INTERN "HELLO") ; #'(INTERN "HELLO") ; ; caught ERROR: ; (INTERN "HELLO") is not a legal function name. ; ; compilation unit finished ; caught 1 ERROR condition debugger invoked on a SB-INT:COMPILED-PROGRAM-ERROR in thread #<THREAD "main thread" RUNNING {1002978CB3}>: Execution of a form compiled with errors. Form: #'(INTERN HELLO) Compile-time error: (INTERN "HELLO") is not a legal function name. Type HELP for debugger help, or (SB-EXT:QUIT) to exit from SBCL. restarts (invokable by number or by possibly-abbreviated name): 0: [ABORT] Exit debugger, returning to top level. (#:EVAL-THUNK) 0] 0 *
(intern "HELLO")ではどうもうまくいかないようだ。
function というのがあるみたいなので、それを使ってみる。
* (function hello) #<FUNCTION HELLO> * (funcall (function hello)) Hello! NIL *
おお、これは期待通りの結果だ。hello という symbol を返すことができればできそうなので intern と組み合わせてみる。
* (funcall (function (intern "HELLO"))) ; in: FUNCALL #'(INTERN "HELLO") ; #'(INTERN "HELLO") ; ; caught ERROR: ; (INTERN "HELLO") is not a legal function name. ; ; compilation unit finished ; caught 1 ERROR condition debugger invoked on a SB-INT:COMPILED-PROGRAM-ERROR in thread #<THREAD "main thread" RUNNING {1002978CB3}>: Execution of a form compiled with errors. Form: #'(INTERN HELLO) Compile-time error: (INTERN "HELLO") is not a legal function name. Type HELP for debugger help, or (SB-EXT:QUIT) to exit from SBCL. restarts (invokable by number or by possibly-abbreviated name): 0: [ABORT] Exit debugger, returning to top level. (#:EVAL-THUNK) 0]
なぜダメなのか、、、(intern "HELLO")が function に渡されている。
直接 intern の結果を funcall に渡すとどうなるか。。。
* (funcall (intern (string-upcase "hello"))) Hello! NIL *
おお、これも期待通り。
他にも、find-symbol というのがあるようで、これを使用することでもできるのを確認。
* (symbol-function (find-symbol (string-upcase "hello"))) #<FUNCTION HELLO> * (funcall (symbol-function (find-symbol (string-upcase "hello")))) Hello! NIL
これくらいでいいだろう。