lengths-list-file の詳細
¶lengths-list-file 関数の中核となるのは、point を defun
単位で前進させる関数と、各 defun 内の単語数と記号数をカウントする関数を含む while
ループです。この中核部分は、ファイルの検索や、point
がファイルの先頭から始まることの確保など、その他のさまざまな処理を行う関数で囲まれている必要があります。関数の定義は次のようになります。
(defun lengths-list-file (filename) "Return list of definitions' lengths within the file named FILENAME. The returned list is a list of numbers. Each number in the list is the number of words or symbols in one function definition."
(message "Working on `%s' ... " filename)
(save-excursion
(let ((buffer (find-file-noselect filename))
(lengths-list))
(set-buffer buffer)
(setq buffer-read-only t)
(widen)
(goto-char (point-min))
(while (re-search-forward "^(defun" nil t)
(setq lengths-list
(cons (count-words-in-defun) lengths-list)))
(kill-buffer buffer)
lengths-list)))
この関数には、処理対象となるファイル名である1つの引数 filename が渡されます。ドキュメントは4行ありますが、インタラクティブな仕様記述はありません。何が起きているのかが見えないとコンピュータが故障したのではないかと不安に思う人がいるため、本体の1行目にはメッセージが記述されています。
次の行には、関数の実行が完了した際にEmacsのフォーカスを現在のバッファに戻すsave-excursionが含まれています。これは、pointが元のバッファに復元されることを前提とした別の関数の中に、この関数を組み込む場合に有用です。
let 式の varlist において、Emacs はファイルを探し、ローカル変数 buffer をそのファイルを含むバッファにバインドします。同時に、Emacs は lengths-list をローカル変数として作成します。
次に、Emacsはバッファにフォーカスを移します。
次の行で、Emacsはバッファを読み取り専用にします。本来であれば、この行は不要です。関数定義内の単語や記号をカウントする関数のいずれも、バッファを変更すべきではありません。それに、たとえ変更されたとしても、バッファが保存されることはありません。この行は、極めて、おそらく過剰なまでの慎重さから生じたものです。慎重な理由は、この関数およびそれが呼び出す関数がEmacsのソースコードに対して動作しており、意図せず変更されてしまうと不都合だからです。言うまでもなく、実験が失敗してEmacsのソースファイルが変更され始めるまで、この行が必要だとは気づきませんでした…
次に、バッファが狭められている場合にそれを広げる処理が続きます。この機能は通常不要です。Emacsは、バッファが存在しない場合は新規に作成しますが、ファイルを参照しているバッファが既に存在する場合はそれを返します。この場合、バッファが狭められている可能性があり、広げる必要があります。完全にユーザーフレンドリーにするなら、制限情報とポイントの位置を保存するようにしたいところですが、そうはしません。
(goto-char (point-min)) 式は、ポイントをバッファの先頭に移動します。
次に、関数の処理が行われるwhileループに入ります。このループの中で、Emacsは各定義の長さを判定し、その情報を含むlengths’リストを構築します。
Emacsはバッファの処理が終わると、そのバッファを破棄します。これはEmacs内部の容量を節約するためです。私が使用していたGNU Emacs
19には300以上の関連ソースファイルが含まれており、GNU Emacs 22には1,000以上の、Emacs
30.2には1,600以上のソースファイルが含まれています。別の関数が、各ファイルにlengths-list-fileを適用します。
最後に、let 式内の最後の式は変数 lengths-list であり、その値が関数全体の値として返されます。
この関数は、通常の手順でインストールして試すことができます。次に、以下の式の後にカーソルを置き、C-x
C-e (eval-last-sexp) と入力してください。
(lengths-list-file "/usr/local/share/emacs/30.2/lisp/emacs-lisp/debug.el")
ファイル名を変更する必要がある場合があります。ここにあるファイルは、GNU Emacs バージョン 30.2 のデフォルトインストールツリー用です。式を変更するには、それを *scratch* バッファにコピーして編集してください。
また、省略されたバージョンではなく、リストの全文を表示するには、以下を実行する必要がある場合があります。
(custom-set-variables '(eval-expression-print-length nil))
(defcustom
を用いた変数の指定を参照してください。その後、lengths-list-file
式を評価します。)
debug.el の lengths ’ list の生成には1秒もかからず、GNU Emacs 30.2 では次のように表示されます:
(79 26 140 34 17 112 81 24 155 54 43 102 21 36 36 117 28 29 102 49 43 208 101 28 22 728 15 27)
(古いマシンを使ったところ、debug.el のバージョン19のlengths’リストの生成に7秒かかり、次のような表示になりました:
(75 41 80 62 20 45 44 68 45 12 34 235)
debug.elの新バージョンは旧バージョンよりもdefunが多く、また私の新しいマシンは古いマシンよりずっと高速だ。)
なお、ファイル内の最後の定義の長さがリストの先頭に来る点に注意してください。