ささだ
先生! 初心者ですわかりません教えてください。
マクロ
Lisp はこれがあるから辞められない、と聞くんですが、使ったことがないのですよ。
R5RS しか知らないんですが、 define-macro ってなんですか?
t-y-scheme には解説がありましたが・・・。
マクロ展開のタイミングは何時?
Scheme:マクロの効用 では、
但し、処理系によって最適化の方策は異なるので、なんでもかんでもマクロにすれば速くなるとは限らない。ナイーブなインタプリタ(ほとんど無いと思うけど)では評価の度にマクロが展開されるかもしれない。モジュールを解釈するコンパイラなら、逆にモジュール内ローカルの関数定義は勝手にインライン展開してくれるかもしれない。
とかかれているんですが、次のような例ではどうしましょう。
(define cont #f) (define-syntax test (syntax-rules () ((test expr) (begin (display "=") (display expr) (newline))))) (test 1) (begin (call/cc (lambda (c) (set! cont c))) (test 100)) (define-syntax test (syntax-rules () ((test expr) (begin (display "*") (display expr) (newline))))) (test 1) (cont 10) ;; DrScheme =1 =100 *1 *100 ;; ==> ここが =100 か *100 か
こんなの無視?(Gauche ではそうなってるようですね)
ちょっと実装について思いを馳せてみると、define-syntax で再定義するときに既に展開したマクロをキャンセルするような何かを作ってみたり。うーむ。それか、そのマクロで既に展開されているかをチェック、つまり変換結果をキャッシュしておいて、それがヒットするならばそれを使う、というような手法なのかなぁ。ああ、いいな、これ。これで行こうかな。(自己完結かよ)
誰もマクロ再定義(とか、組込み関数再定義とか)なんてしねーよ、だからこんなの無視して問題ない、っていう論旨なら、それはそれで納得します。
組み込み関数での同様の問題は
(begin (display (+ 1 2)) (set! + -) (display (+ 1 2)))
です。
Rucheme では、こういうのどうすればいいのかわかんなかったので、「ナイーブな」実装になってます。
- Shiro (2003/09/12 23:54:09 PDT): いくつか要素が混ざっているので個別に。
- トップレベル環境のセマンティクスはR5RSで明確に定義されていないことに 注意してください。特に、コンパイルと実行が完全に分かれている処理系と、 逐次解釈する処理系とではトップレベルの扱い方は大きく異なるでしょう。
- トップレベル環境でつかまえた継続のセマンティクスはまるきり 未定義なので、議論にならないです(コンパイルと実行が完全に分かれている モデルを想定してみて下さい)。
- 組込み関数や組込み構文の再定義は、それを許す処理系が多いですね。 自分で拡張できるのがSchemeの良いところですから。但し、そうやって再定義した 束縛が、既に定義されている関数に影響をあたえるかどうかという点は未定義です。
- マクロはコンパイル時に展開するというのが一般的な認識だと思います。 従って、マクロの再定義は既に定義された関数には影響を及ぼさないのが 普通です。ですが、実行時にマクロをいちいち解釈する処理系があっても 構わないので、そういう処理系では再定義は影響をあたえるでしょうね。
- なお、上の (set! + 1) の例は組込み関数の再定義じゃなくて代入ですね。
- ささだ ありがとうございました。大変よくわかりました。と言いたいんですが、もう少し考えないとよくわからないっぽいです。でも、感謝する気持ちに変わりはありません。
- コンパイルしても、そのときの環境から取ってくるコードが出力されるのだと思っておりました。
- トップレベルに捕まえた継続はどうするべきか頭を悩ませてたんですが、そうか、定義されてなかったのか。
- 組み込み関数の再定義の件ですが、次のように訂正します。
(begin (define + -) (+ 1 2)) ;;=> Gauche: 3
あれ、でもこれは -1 なんだな。
(let ((+ -)) (+ 1 2)) ;;=> Gauche: -1
あ、begin での定義はトップレベルでの定義と同じなのか。失礼しました。
- 引数が値じゃ困るからね。