🔙 Back to Top

Mon Jul 22 16:30:14 JST 2013

Scheme (R5RS) マクロ

syntax-rules のキーワードは無くてもいいようなものにしか使えない 区切りとしては決して使えない

  (define-syntax two-list
    (syntax-rules (delminator)
      ((two-list x ... delminator y ...)
        (values (list x ...) (list y ...)))))

... というのは直後に括弧閉じが来るような場所にしか来られないので 上はコンパイルエラーを起こす.

でも大体上みたいなことがしたい.

(two-list 1 2 3 delminator 4 5) => (values (list 1 2 3) (list 4 5))

syntax-rulesでもってそれを実現するコードはちょっと,すぐには書けそうにない.

現実的には (two-list (1 2 3) delminator (4 5)) こう書いてもらえば解決する でもちょっとね.括弧は出来るだけ減らしたいしね

マクロで相互再帰とかして一応動くコードを書こうと試行錯誤したけど やっぱり描けませんでした

define-macroは,引数をクオートされた状態で,普通の scheme 手続きを 用いて操作できるから,ほんと何でもできていい

(use srfi-1)
(define-macro (two-list . ls)
  (receive (a b) (split-at ls (list-index (cut equal? '* <>) ls))
  `(values (list ,@a) (list ,@(cdr b)))))
gosh> (macroexpand '(two-list 1 2 3 * 4 5))
(values (list 1 2 3) (list 4 5))
gosh> (two-list 1 2 3 * 4 5)
(1 2 3)
(4 5)

で, やりたかったのはこれ

gosh> (begin 1 2 3)
3
gosh> (begin 1 2 a where (a 3))
3

Haskellの where

(begin a ... where (var val) ...)

というのはただ

(let* ((var val) ...) a ...)

に変換してる. let* だから,where後で定義する変数は相互に参照できるわけではないので注意

コードは以下の通り begin のオーバーロードになってる

(use srfi-1)
(define-macro (begin . body)
  (let1 idx (list-index (cut equal? 'where <>) body)
  (if idx
      (receive (a b) (split-at body idx) `(let* ,(cdr b) ,@a))
      `(let* () ,@body))))