Shammer's Philosophy

My private adversaria

文字列と同名の関数を呼出す

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

これくらいでいいだろう。