Next: Common Lispとの互換性, Previous: アサーション, Up: Top [Contents][Index]
defun*
やloop
、setf
のようなこのパッケージの高度な
機能の多くは、Lispマクロとして実装されている。バイトコンパイルされた
コードでは、これらの複雑な表記法は単純で効率のよい同等のLispコードに
展開される。たとえば、フォーム
(incf i n) (push x (car p))
は、コンパイル時に下記のLispフォームへ展開される:
(setq i (+ i n)) (setcar p (cons x (car p)))
これらは、Lispでそれぞれの作用を行なう最も効率的な方法である。
したがって、コンパイルされたコードの中では、より読みやすいincf
やpush
フォームを使うことに性能上の不利益はない。
他方、インタプリトされたコードは、実行されるたびにこれらの
マクロを展開しなければならない。この理由により、マクロを猛烈に使う
コードはコンパイルされることが強く推奨される(このマニュアルで“関数”
ではなく“特殊フォーム”とラベルを付けられている機能はマクロである)。
incf
を100回使うループは、コンパイルされた場合はかなり速く
実行し、マクロ展開が100回生成され、使用され、捨て去られる
必要がないので、ガベジコレクションもより少なくなるだろう。
cl-prettyexpand
関数を使うことで、どのようにマクロが展開するかを
知ることができる。
この関数は、引数として1つのLispフォームを取り、現バッファ(字下げが
適正に働くようにLispモードでなければならない)にきちんと
フォーマットされたそのコピーを挿入する。フォーム中に現れるすべてのLisp
マクロも展開する。この関数を使う最も容易な方法は、*scratch*
バッファへ行き、たとえば以下をタイプして、
(cl-prettyexpand '(loop for x below 10 collect x))
閉じ括弧の直後でC-x C-eをタイプすることである; 展開
(block nil (let* ((x 0) (G1004 nil)) (while (< x 10) (setq G1004 (cons x G1004)) (setq x (+ x 1))) (nreverse G1004)))
がバッファへ挿入されるだろう(block
マクロはインタプリタと
コンパイラで異なって展開されるので、cl-prettyexpand
はそれをそのままにしている。一時変数G1004
はgensym
によって作られた)。
付加引数fullが真の場合、block
やeval-when
、
コンパイラマクロを含むすべてのマクロが展開される。展開は、
formがコンパイルされるファイル中の
トップレベルフォームであるかのようになされる。たとえば、
(cl-prettyexpand '(pushnew 'x list)) -| (setq list (adjoin 'x list)) (cl-prettyexpand '(pushnew 'x list) t) -| (setq list (if (memq 'x list) list (cons 'x list))) (cl-prettyexpand '(caddr (member* 'a list)) t) -| (car (cdr (cdr (memq 'a list))))
adjoin
やcaddr
、member*
はすべて、共通の
場合にそれらを最適化するための組み込みのコンパイラマクロを
持っていることに注意せよ。
Common Lispへの追従は一般に、効率を犠牲にしていない。いくつかの例外が、
周辺的な非互換性を犠牲にして相当な利益が可能な場合になされた。一例は、
キーワード引数を走査するためのmemq
(これは
バイトコンパイラによって非常に効率的に扱われる)の使用である; これは、
キーワードシンボルがキーワードとデータの値に同時に使われるようなまれな
場合に混乱する可能性がある。これが実用的なコードの中で
起こることはきわめてありそうになく、memq
の使用は、キーワード
引数を伴う関数が&optional
引数を使う関数とほとんど同じくらい
速いことを許す。
(Steeleの本で具体化された)Common Lisp標準は、従うプログラム中では
発生しないことになっている状況を示すために、句“その場合、それは
エラーである”を使う; 処理系は、これらの状況中でエラーを
通知することを、必須ではないが強く奨励される。
このパッケージはときどき、コンパクトさと効率の利益のために、このような
エラーチェックを省略する。たとえば、do
変数指定子は、1つか2つ、
あるいは3つのフォームのリストということになっている; 余分の
フォームは、このパッケージでは文法エラーを通知するのではなく
無視される。endp
関数は単に、このパッケージではnull
の
同義語である。キーワード引数を取る関数は、奇数個の引数を受け入れ、
値nil
がそれに続くかのように末尾のキーワードを扱う。
(defun*
やその友人によって処理されるとき)引数リストは、今述べた
重要でない点を除いて、厳しくチェックされる; 特に、キーワード
引数は有効性をチェックされ、&allow-other-keys
と
:allow-other-keys
は完全に実装されている。キーワード有効性
チェックは、わずかに時間を消費する(しかしバイトコンパイルされた
コードではそれほど悪くはない); チェックを省略するために
&allow-other-keys
を使える。find
やmember*
のようなこのパッケージで定義された関数は、そのキーワード引数の有効性
チェックを行なう。
Emacs 18に付属のバイトコンパイラは、ファイルのトップレベル位置
(すなわち、defun
や他の囲むフォームの外)に現れるマクロの展開に
失敗する。これは、defun*
やeval-when
、defstruct
のようなトップレベルマクロを使うプログラムに、悲惨な
結果をもたらすだろう。この問題を回避するために、CLパッケージは、
トップレベルマクロを展開するようにEmacs 18のコンパイラに
パッチをあてる。あなた自身のマクロがトップレベルコンテキストで使われる
場合も、このパッチが適用されるだろう。このパッチは、すでに類似の
パッチが適用されたEmacs 18コンパイラの版に害を与えないし、
Jamie ZawinskiとHallvard Furusethによって書かれた最適化Emacs 19
コンパイラには影響しない。パッチはEmacsのメモリ中の
バイトコンパイラコードへ適用され、ディスク上の格納されている
bytecomp.elcファイルへ適用されるのではない。
(Emacs 18のための)Emacs 19コンパイラは、
archive.cis.ohio-state.edu
のようなさまざまなEmacs Lisp
アーカイブサイトで利用できる。その使用は大いに推奨される; Common Lisp
マクロの多くは、最適化により改善される可能性があるコードを発する。
特に、block
は(明示的であろうと、defun*
やloop
のような構文要素中に暗黙であろうと)かなりの実行時の不利益を持つ;
最適化コンパイラは、ブロックの内部でreturn
やreturn-from
によって実際に参照されないblock
を取り除く。