マクロは、(それ自身では)結果の計算を行なわない Lisp フォームです。そのかわり、 (結果を生み出す) より複雑なフォームを作るためのものです。 Lisp がマクロを受け 取ると、より複雑なフォームを得るためマクロの本体を評価します。 (その後)結果を 求めるため、その(訳注:作り出された)フォームを評価します。
マクロを用いる場合の例として、次のような場合を考えてみて下さい。 (何かをカウン トする変数を持ち)それをインクリメントしたい場合がしばしばあります。 C は、プロ グラマが x = x + 1 と書かなくてもすむよう x++ というものを持っていますが、 GNU Emacs Lisp は、そのようなものを持っていません。このため常に (setq x (1+ x)) と 書かなくてはいけません。
この問題を解消し、 (よりシンプルに短かく) (inc x) と書くことができるようマクロ を書くことができます。これを行なうためのマクロを以下に示します。
(defmacro inc (var) (list 'setq var (list '1+ var)))
(inc x) と呼ぶと、このマクロは (評価すると正しく望み通りの結果を与えるフォーム である) リスト (setq x (1+ x)) を作ります。 Lisp はこれを評価し、望み通りの結 果(つまり、変数のインクリメント) を得ます。
マクロフォームの実行は次のように行なわれます。 (フォームの)アーギュメントは評 価せずにマクロに渡されます。マクロはそれを(マクロの)アーギュメントにバインド し、本体を実行します。マクロの返す式は展開(訳注:expansion) と呼ばれます。この 後、Lisp は (あたかもそれが最初から与えられていたかのように) この展開を評価し ます。
返される式は(普通に)評価されるため、(別の)マクロ呼び出しを含んでいてもかまいま せん。 (普通には存在しないかもしれませんが)リカーシブなマクロ呼び出しも誤まり ではありません。
バイトコンパイルされたマクロは、(実行時ではなく)コンパイル時に展開されます。こ のためバイトコンパイルされたマクロの呼び出しは、同じ操作を行なう関数呼び出しよ り速くなります。しかし、バイトコンパイルされるコードでは、マクロは呼び出される 前に定義されてあるよう注意しなくてはいけません。マクロ定義に circular dependency が存在する場合、定義ファイルを最初にロードするようにするのが(最も) 簡単な方法です。(訳注:??)
(簡単に)繰返し(訳注:iteration)を行なえるようにするために、書かれうる、(複雑 な)マクロの例を次に示します。
(defmacro for (var from init to final do &rest body) "Execute a fimple \"for\" loop, e.g., (for i from 1 to 10 do (print i))."
(list 'let (list (list var init)) (cons 'while (cons (list '<= var final) (append body (list (list 'inc var))) )))) => for
(for i from 1 to 3 do (setq square (* i i)) (princ (format "\n%d %d i square)))
=> (set ((i 1)) (while (<= i 3) (setq square (* i i)) (pirnc (format "%d %d" i square)) (inc i)))
-> 1 1 2 4 3 9 => nil
このマクロは、(Pascal に見られるような)シンプルな "for" ループを書けるようにし ます。シンボル from, to, do は(全て) このマクロにおける "シンタックスシュガー" であり、(全て)無視される点に注意して下さい。
(しかし) のマクロは、繰り返しごとに final を評価する欠点を持っています。 final がコンスタントであれば問題はありませんが、より複雑なフォームの場合、例え ば (long-complex-calculation x) のような場合、これはかなり実行を遅らせてしまう 可能性を持ちます。副作用を持つ場合は、さらにひどいことになる可能性があります。
マクロを書く場合、そのアーギュメントが (プログラマが期待した回数より)多く評価 されることのないよう注意し、それを保証しなくてはいけません。 (for マクロに対す る)より良い展開を以下に示します。
(let ((i 1) (max 3)) (while (<= i max) (setq square (* i i)) (princ (format "%d %d" i square)) (int i)))
ここでは max は intern されてないシンボルです (セクション 8.4 [intern]、ページ 68参照)。
(この展開を作る方法を示す!!)