で、マクロってどんなことが出来るのでしょう? 手短にいうと、新たな構文を導入し、既存の式への変換ルールを与えることが出来ます。 これは、C 言語の Macro でも同じです。変換ルールの記述能力が圧倒的に異なりますが。
さきほど紹介した遅延評価は、実はラムダ式を使えば似たような事が出来ると、 以前ラムダ式の練習問題でやりました。以下の通りです。 head, tail が car, cdr に相当します。
(define (infinite-enumerate n) (cons n (lambda () (infinite-enumerate (+ n 1))))) (define (head inf-list) (car inf-list)) (define (tail inf-list) (let ((rest (cdr inf-list))) (rest)))ここでは、head や tail は普通の関数呼出しの様に作る事ができています。 あと、
(lazy-cons n (infinite-enumerate (+ n 1)))の様に書くことができれば、 head, tail, lazy-cons をつかって簡単に遅延評価版リストを使えそうです。 しかし、lazy-cons という関数を定義することは出来ません。 なぜなら、関数呼出しの場合、 引数である(infinite-enumerate (+ n 1)) をまず評価してしまうからです。
この場合やりたいことは、
(lazy-cons n (infinite-enumerate (+ n 1))) => (cons n (lambda () (infinite-enumerate (+ n 1))))に変換することです。そこで DrScheme では以下のようにマクロを書くことが出来ます。
(define (lazy-cons-expander x y) ; これは、lazy-cons をどの様に展開するかを示す関数 (list 'cons x (list 'lambda '() y))) ; lazy-cons-expander の 使用例 ; > (lazy-cons-expander 'n '(infinite-enumerate (+ n 1))) ; (cons n (lambda () (infinite-enumerate (+ n 1)))) ; lazy-cons というマクロは lazy-cons-expnader で展開して下さいという命令 (define-macro lazy-cons lazy-cons-expander) ; これで、lazy-cons というマクロが使用可能になり、勝手に先程の様に展開してくれます。 ; lazy-cons 使用例 (define (infinite-enumerate2 n) (lazy-cons n (infinite-enumerate2 (+ n 1))))これで、lazy-cons, head, tail で見た目もきれいに自作遅延評価が使えるようになりました。
> (require-library "match.ss" "mzlib") ; mzlib というcollection にある "match.ss" を使う > (define (calc-total-weight x) ; 二分木の総重量をもとめる (match x ; x が [('leaf weight) ; ('leaf weight) という形にあうなら, ; つまり、先頭が leaf というシンボルで長さが 2 のリストなら ; weight という変数に weight が該当する部分を束縛し、 weight] ; weight を返す。 [('node left right) ; ('node left right) という形にあうなら、 ; つまり、先頭が node というシンボルで長さ 3 のリストなら ; 第二、三要素を left, right に束縛し、 (+ (calc-total-weight left) ; 以下の式の値を返す (calc-total-weight right))])) > (calc-total-weight '(node (leaf 3) (node (leaf 4) (leaf 5)))) 12
99.10.7/ Tomio KAMADA: kamada@cs.kobe-u.ac.jp