Shiro Kawai
shiro****@lava*****
2004年 2月 20日 (金) 19:12:23 JST
From: NIIBE Yutaka <gniib****@m17n*****> Subject: [Anthy-dev 578] Re: uimのキーバインド定義を簡単に Date: Fri, 20 Feb 2004 18:39:22 +0900 (JST) > TOKUNAGA Hiroyuki wrote: > > マクロを使わずに呼び出した側の環境(もしくはトップレベルの環境)に変数 > > を束縛する方法ってあるんでしょうか? > > eval を使うというのが答えです。たぶん。 > > 僕も Emacs Lisp から Lisp にはいったので, このまわりで同様なことについ > てこれまで悩んでました。先日, 川合さんに聞いてやっぱりそうかと納得した > ところです。 > > 「マクロでできるはず」と考えるのですが, マクロでも(処理系によっては)難 > しいです。Gauche, Guile でやってみたけど, 僕にはできませんでした。 > > 例として単純化してみましょう。例えば, プログラムの断片に > > (set! a 1) (set! b 1) (set! c 1) (set! d 1) > > とあったら, まとめようとかんがえて(Emacs Lisp 流儀では) > > (for-each l '(a b c d) > (my-set-defined-as-macro (car l) 1)) > > としてしまう。それでマクロで展開して `(set! ,symbol ,expr) とできない > かと思いますよね。普通。ここで僕はつまずきました。できませんでした。 Schemeでは原則として「プログラムの変換(生成)」と「プログラムの 評価(実行)」のステージがはっきり分かれています。そのおかげで、 プログラムを変換していって行き着く先のコアな言語でのみセマンティクスを 定義しておけば、全ての有効なSchemeのプログラムのセマンティクスを 確定することができるのです。 マクロはプログラム変換のステージに作用するので、プログラムを実行 してみなければわからない情報は使うことができません。 言い替えれば、プログラム本体の評価前に望まれるプログラムが決定できる ならば、マクロが使えます。 ですんで、上記のような例では、set!する対象がプログラム実行前に確定 していれば、マクロが使えます。 R5RSマクロならこう: (define-syntax set-1 (syntax-rules () ((set-1 var ...) (begin (set! var 1) ...)))) (set-1 a b c d) R5RSマクロは静的な解析を重視しているため、マクロ展開時に任意の コードを走らせることができませんが、legacyマクロを使えば、 例えばファイルからset!すべき変数のリストを読んで来ることもできます。 (define-macro (set-1-from-file file) (let ((vars (with-input-from-file file read))) `(begin ,@(map (cut list 'set! <> 1) vars)))) マクロが使えない(evalにしなければならない)場合とは、 プログラム本体の評価が始まってからプログラムが生成されるような 場合です。上の例で言えば、ユーザがインタラクティブに与えた ファイル名を用いてコードを生成したい、といった場合ですね。 > 本質的にこれはその環境で eval せねばなりません。だからマクロではなくて, > 上記の例では, 関数として, > > (define (my-set symbol expr) > (eval `(set! ,symbol ,expr) (interaction-environment))) > ^^^^^^^^^^^^^^^^^^^^^^^^^ ここは処理系に依る > と作ることになります。 interaction-environmentはR5RSに定義されているので、一応 ポータブルと言えると思います。R4RS以前の処理系ですと、evalが 第2引数をとらなかったりしますけれど。 --shiro