util.match は便利だけどどう展開されるの?
Gauche の util.match (Andrew Wright の match)はとても便利で自前の処理系にも移植して使っています。
自前のコンパイラ内でも match を使っていてコードの可読性と拡張性を高めてくれています。
ただ match がどう展開されるかを見たことがなかったので試してみました。
match はただのマクロなので Gauche の %macroexpand を使えば展開後の形が分かります。
(%macroexpand (match x [('CONSTANT y 'PUSH . rest) `(CONSTANT_PUSH ,y ,@rest)] [('REFER_LOCAL0 y 'PUSH . rest) `(CONSTANT_PUSH0 ,y ,@rest)])) (if (pair? x) (if (equal? (car x) 'CONSTANT) (if (and (pair? (cdr x)) (pair? (cddr x)) (equal? (caddr x) 'PUSH)) ((lambda (y rest) `(CONSTANT_PUSH ,y ,@rest)) (cadr x) (cdddr x)) (match:error x)) (if (and (equal? (car x) 'REFER_LOCAL0) (pair? (cdr x)) (pair? (cddr x)) (equal? (caddr x) 'PUSH)) ((lambda (y rest) `(CONSTANT_PUSH0 ,y ,@rest)) (cadr x) (cdddr x)) (match:error x))) (match:error x))
let を使わず lambda の呼び出しなのはなぜだろうか?自前の処理系の match はそのうち let を吐くようにしよう。
x が pair? を満たすかどうかは一度しかチェックされないのが良いですね。
not を使ってみた。
おお。手で比較を書いでも同じようになるのでこれは match を信用しても良さそうです。
(%macroexpand (match x [((and y (not 'CONSTANT)) NUMBERP) y])) (if (pair? x) (if (equal? (car x) 'CONSTANT) (match:error x) (if (and (pair? (cdr x)) (null? (cddr x))) ((lambda (y NUMBERP) y) (car x) (cadr x)) (match:error x))) (match:error x))
欲を言えばシンボルを使っているときは比較手続きを eq? にしてくれないだろうか。