Next: , Previous: , Up: Top   [Contents][Index]


Appendix A 効率の配慮

A.1 マクロ

defun*loopsetfのようなこのパッケージの高度な 機能の多くは、Lispマクロとして実装されている。バイトコンパイルされた コードでは、これらの複雑な表記法は単純で効率のよい同等のLispコードに 展開される。たとえば、フォーム

(incf i n)
(push x (car p))

は、コンパイル時に下記のLispフォームへ展開される:

(setq i (+ i n))
(setcar p (cons x (car p)))

これらは、Lispでそれぞれの作用を行なう最も効率的な方法である。 したがって、コンパイルされたコードの中では、より読みやすいincfpushフォームを使うことに性能上の不利益はない。

他方、インタプリトされたコードは、実行されるたびにこれらの マクロを展開しなければならない。この理由により、マクロを猛烈に使う コードはコンパイルされることが強く推奨される(このマニュアルで“関数” ではなく“特殊フォーム”とラベルを付けられている機能はマクロである)。 incfを100回使うループは、コンパイルされた場合はかなり速く 実行し、マクロ展開が100回生成され、使用され、捨て去られる 必要がないので、ガベジコレクションもより少なくなるだろう。

cl-prettyexpand関数を使うことで、どのようにマクロが展開するかを 知ることができる。

Function: cl-prettyexpand form &optional full

この関数は、引数として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 はそれをそのままにしている。一時変数G1004gensym によって作られた)。

付加引数fullが真の場合、blockeval-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))))

adjoincaddrmember*はすべて、共通の 場合にそれらを最適化するための組み込みのコンパイラマクロを 持っていることに注意せよ。

A.2 エラーチェック

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を使える。findmember* のようなこのパッケージで定義された関数は、そのキーワード引数の有効性 チェックを行なう。

A.3 最適化コンパイラ

Emacs 18に付属のバイトコンパイラは、ファイルのトップレベル位置 (すなわち、defunや他の囲むフォームの外)に現れるマクロの展開に 失敗する。これは、defun*eval-whendefstruct のようなトップレベルマクロを使うプログラムに、悲惨な 結果をもたらすだろう。この問題を回避するために、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 のような構文要素中に暗黙であろうと)かなりの実行時の不利益を持つ; 最適化コンパイラは、ブロックの内部でreturnreturn-from によって実際に参照されないblockを取り除く。