2010/06/02

syntax-rules: bind-variables

こういうの。
(bind-variables ((a 1)
            (b)
            c
            e
            (d (+ a 3)))
           (list a b c d e))
;; -> (1 #f #f 4 #f)

で、自分なりに書いてはみたものの・・・。かなり苦戦しました。
しかも複雑になってしまって、何が何やら。動いてはいるっぽいものの、これじゃーちょっと・・・。それに何よりダサい。
;; vind-vars
;; vind-variables http://web.archive.org/web/20060616054033/home.comcast.net/~prunesquallor/macro.txt
(define-syntax bind-vars
(syntax-rules ()
((_ () body ...)
(let ()
body ...))
((_ ((var init)) body ...)
(let ((var init))
body ...))
((_ ((var1 init1)(var2 init2) ...) body ...)
(bind-vars ((var1 init1))
(bind-vars ((var2 init2) ...)
body ...)))
((_ ((var1)) body ...)
(bind-vars ((var1 #f)) body ...))
((_ ((var1)(var2) ...) body ...)
(bind-vars ((var1))
(bind-vars ((var2) ...)
body ...)))
((_ ((var1)(var2 init2) ...) body ...)
(bind-vars ((var1))
(bind-vars ((var2 init2) ...)
body ...)))
((_ (var) body ...)
(bind-vars ((var))
body ...))
((_ (var1 var2 ...) body ...)
(bind-vars (var1)
(bind-vars (var2 ...) body ...)))
((_ (var1 (var2) ...) body ...)
(bind-vars (var1)
(bind-vars ((var2) ...) body ...)))
((_ (var1 (var2 init2) ...) body ...)
(bind-vars (var1)
(bind-vars ((var2 init2) ...) body...)))))
(bind-vars ((a 1)
(b)
c
e
(d (+ a 3)))
(list a b c d e))
;; (1 #f #f 4 #f)
(let ((a 1))
(bind-vars ((b)
c
e
(d (+ a 3)))
(list a b c d e)))
;; (1 #f #f 4 #f)
(let ((a 1))
(let ((b #t))
(bind-vars (c
e
(d (+ a 3)))
(list a b c d e))))
;; (1 #t #f 4 #f)
(bind-vars (a b c d e)
(list a b c d e))
;; (#f #f #f #f #f)
(bind-vars ((a)(b)(c)(d)(e))
(list a b c d e))
;; (#f #f #f #f #f)
(bind-vars (a (b 1)(c 2) d (e))
(list a b c d e))
;; (#f 1 2 #f #f)
view raw bind-vars.scm hosted with ❤ by GitHub


お手本はこちら。うーん、だいぶスッキリしていますね。「...」だと、こうは書けないですよね。。場合によってはドット対表現で書いた方がスッキリするんですね。
;; vind-variables http://web.archive.org/web/20060616054033/home.comcast.net/~prunesquallor/macro.txt
(define-syntax bind-variables
(syntax-rules ()
((bind-variables () form . forms)
(begin form . forms))
((bind-variables ((variable value0 value1 . more) . more-bindings) form . forms)
(syntax-error "bind-variables illegal binding" (variable value0 value1 . more)))
((bind-variables ((variable value) . more-bindings) form . forms)
(let ((variable value)) (bind-variables more-bindings form . forms)))
((bind-variables ((variable) . more-bindings) form . forms)
(let ((variable #f)) (bind-variables more-bindings form . forms)))
((bind-variables (variable . more-bindings) form . forms)
(let ((variable #f)) (bind-variables more-bindings form . forms)))
((bind-variables bindings form . forms)
(syntax-error "Bindings must be a list." bindings))))
(bind-variables ((a 1)
(b)
c
e
(d (+ a 3)))
(list a b c d e))
;; (1 #f #f 4 #f)
(let ((a 1))
(bind-variables ((b)
c
e
(d (+ a 3)))
(list a b c d e)))
;; (1 #f #f 4 #f)
(let ((a 1))
(let ((b #t))
(bind-variables (c
e
(d (+ a 3)))
(list a b c d e))))
;; (1 #t #f 4 #f)
(bind-variables (a b c d e)
(list a b c d e))
;; (#f #f #f #f #f)
(bind-variables ((a)(b)(c)(d)(e))
(list a b c d e))
;; (#f #f #f #f #f)
(bind-variables (a (b 1)(c 2) d (e))
(list a b c d e))
;; (#f 1 2 #f #f)


追記

書けてるー(笑)なるほどー。
(define-syntax bind-variables
(syntax-rules ()
((_ () form ...)
(begin form ...))
;; ここはオリジナルの syntax-error がどんな挙動か分からないので割愛
;; error で置き換えてみたが、本体に form ... が無い、と怒られた
;; ((_ ((var val0 val1 ...) ...) form ...)
;; (error "bind-variables illegal binding" (var val0 val1 ...)))
((_ ((var val) more-bindings ...) form ...)
(let ((var val)) (bind-variables (more-bindings ...) form ...)))
((_ ((var) more-bindings ...) form ...)
(let ((var #f)) (bind-variables (more-bindings ...) form ...)))
((_ (var more-bindings ...) form ...)
;; ここは、本当は上のパターンに差し替えたほうが綺麗かも
(let ((var #f)) (bind-variables (more-bindings ...) form ...)))
((_ bindings form ...)
(error "Bindings must be a list." bindings))
))
;; ;; 実行例
;; > (bind-variables ((a 1)
;; (b)
;; c
;; e
;; (d (+ a 3)))
;; (list a b c d e))
;; (1 #f #f 4 #f)
;; > (let ((a 1))
;; (bind-variables ((b)
;; c
;; e
;; (d (+ a 3)))
;; (list a b c d e)))
;; (1 #f #f 4 #f)
;; > (let ((a 1))
;; (let ((b #t))
;; (bind-variables (c
;; e
;; (d (+ a 3)))
;; (list a b c d e))))
;; (1 #t #f 4 #f)
;; > (bind-variables (a b c d e)
;; (list a b c d e))
;; (#f #f #f #f #f)
;; > (bind-variables ((a) (b) (c) (d) (e))
;; (list a b c d e))
;; (#f #f #f #f #f)
;; > (bind-variables (a (b 1) (c 2) d (e))
;; (list a b c d e))
;; (#f 1 2 #f #f)
;; >


プログラミングGauche

5 件のコメント:

  1. R5RS においては、 syntax-rules のパターン部にドット対を用いた表現が許されますが、テンプレート部をドット対で組み立てるのは未定義だとされています。
    なので、なるべく ... (ellipsis) を使った表現が好ましいと思います。
    とは言うものの、 R6RS では問題なく使えますし、 R5RS を名乗る処理系の中でテンプレート部にドット対表現が使えないものを見付けられないので実質的には問題にならないでしょうけど。

    返信削除
  2. いつも教えて頂いてありがとうございます!
    勉強になります。

    >R5RS においては、 syntax-rules のパターン部にドット対を用いた表現が許されますが、テンプレート部をドット対で組み立てるのは未定義だとされています。

    えー!そうなんですか・・・。また微妙な・・・。

    やっぱり、R5RSを読まないとダメですね。。「いつか実装する時でいいや。」と思っていましたが、そうもいかないですね^^;

    返信削除
  3. 「テンプレート部に」というよりは「Scheme の式に」と言った方が正確でした。
    例えば (display . ('a)) とすれば普通に a を表示します。 しかし、 R5RS にある手続き呼出しの構文は ( ...) という形式だけです。 ( . ( ...)) と言う形式で手続きが呼出せる根拠がないのです。
    R6RS でこれが問題なくなるのは、 Scheme のプログラムがS式である旨が明記されたからです。 S式としては書き方がどっちであっても同じと見做せるので問題ありません。
    逆に言えば R5RS では Scheme のプログラムはS式でないことになります。 (少くとも明記されてはいません。) これは結構衝撃的な話です。
    CommonLisp 派が「Scheme は Lisp ではない」と主張する根拠のひとつとして挙げていたのでそれなりに有名な話ではあるのですが、 R5RS だけをよほどきちんと読んでも気付ける人はかなり少ないんじゃないかと思います。

    返信削除
  4. あれ、コメント中に山括弧使ったら消えちゃった。
    ふたつ目の段落だけ再度書きます。
    例えば (display . ('a)) とすれば普通に a を表示します。 しかし、 R5RS にある手続き呼出しの構文は ( 演算子 オペランド ...) という形式だけです。 ( 演算子 . (オペランド ...)) と言う形式で手続きが呼出せる根拠がないのです。

    返信削除
  5. >逆に言えば R5RS では Scheme のプログラムはS式でないことになります。 (少くとも明記されてはいません。) これは結構衝撃的な話です。

    めちゃくちゃ衝撃的な話ですねそれ!(笑)

    返信削除