Scheme の手続きを動的に
やりたいことはすごい簡単なんだけど、やり方がわからない。
(define op #(+ - * /)) (define calc (lambda (kind lhs rhs) ((vector-ref op kind) lhs rhs))) (calc 0 1 2)
手続きをベクタとかリストに持っておいて、それを取り出して実行したい。
Gauche だと以下のようなエラーメッセージ。
gosh> (calc 0 1 2) *** ERROR: invalid application: (+ 1 2) Stack Trace:
期待通りに見えるんだけど、駄目。
Racket だと以下のようなエラーメッセージ。
> (calc 0 1 2) procedure application: expected procedure, given: '+; arguments were: 1 2
同じようなメッセージ。若干 '+ というのが気になるが、クオートされてるわけではないはず。
原因を特定するために、単純化したものを書いてみる。
((car (list +)) 1 2) ; OK ((vector-ref (vector +) 0) 1 2) ; OK ((car '(+)) 1 2) ; NG ((vector-ref #(+) 0) 1 2) ; NG
ここで少し原因に近づいた。
(list +) はよくて '(+) は駄目、(vector +) はよくて #(+) は駄目だということ。そもそも、ここでは #(+) なのか '#(+) なのかもよくわかってない。
R5RS によると、#(0 (2 2 2 2) "Anna") はベクタの外部表現で、ベクタ定数は '#(0 (2 2 2 2) "Anna") というようにクオートしなければならないと書かれているが・・・。
僕の認識では、クオートが必要なのは、その S 式が評価されると困る場合であって、つまり '(+) はクオートがないと足し算で 0 と評価されてしまうため必要だけど、#(+) は評価される心配がないので、クオートはいらない。
最初の例は、以下のように書けばできるというのはわかったんだけど、何で #(+ - * /) は駄目で (vector + - * /) は駄目じゃないのか、その理屈を知りたい。
(define op (vector + - * /)) (define calc (lambda (kind lhs rhs) ((vector-ref op kind) lhs rhs))) (calc 0 3 4)