Go to the first, previous, next, last section, table of contents.

マクロ

マクロは、(それ自身では)結果の計算を行なわない 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参照)。

(この展開を作る方法を示す!!)


Go to the first, previous, next, last section, table of contents.