14.6 lengths-list-file 详解

lengths-list-file 函数的核心是一个包含 while 循环的结构,其中包含一个函数来逐个 defun 地将 point 前进,以及一个函数来计算每个 defun 中的单词和符号数。这个核心必须被包裹在执行各种其他任务的函数中,包括查找文件并确保 point 位于文件的开头。该函数定义如下:

(defun lengths-list-file (filename)
  "返回 FILE 中定义长度的列表。
返回的列表是数字的列表。
每个数字是一个函数定义中的单词或符号的数量。"
  (message "正在处理 `%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)))

该函数接受一个参数,即它将操作的文件的名称。它有四行文档,但没有交互规范。由于人们担心如果他们看不到任何操作就会认为计算机出了问题,函数体的第一行是一个消息。

接下来的一行包含一个 save-excursion,在函数完成时将 Emacs 的注意力返回到当前缓冲区。这对于嵌套在假设 point 被恢复到原始缓冲区的另一个函数中的情况是有用的。

let 表达式的变量列表中,Emacs 找到文件并将局部变量 buffer 绑定到包含文件的缓冲区。同时,Emacs 创建了 lengths-list 作为局部变量。

接下来,Emacs 将注意力切换到缓冲区。

在接下来的一行中,Emacs 将缓冲区设置为只读。理想情况下,这一行是不必要的。在计算函数定义中的单词和符号数的函数中,缓冲区不应该发生更改。此外,即使更改了缓冲区,它也不会被保存。这一行完全是对极度谨慎的后果。对谨慎的原因是,这个函数及其调用的函数处理 Emacs 的源代码,并且如果它们被意外修改,则会很不方便。不用说,在一个实验出现问题并开始修改我的 Emacs 源文件之前,我并没有意识到需要这一行 …

接下来是调用扩展缓冲区的函数,如果它被缩小了的话。通常情况下,这个函数是不需要的 - Emacs 如果还没有存在缓冲区,就会创建一个新的;但是如果已经存在了一个访问文件的缓冲区,Emacs 就返回那个缓冲区。在这种情况下,缓冲区可能被缩小,必须被扩展。如果我们想要完全用户友好,我们会安排保存约束和 point 的位置,但我们不会这样做。

(goto-char (point-min)) 表达式将 point 移动到缓冲区的开头。

然后是一个 while 循环,在循环中执行函数的工作。在循环中,Emacs 确定每个定义的长度并构造包含该信息的长度列表。

在处理完缓冲区后,Emacs 将其关闭。这是为了在 Emacs 中保存空间。我使用的 GNU Emacs 19 版本包含了 300 多个感兴趣的源文件;GNU Emacs 22 包含了一千多个源文件。另一个函数将应用 lengths-list-file 到每个文件。

最后,在 let 表达式中的最后一个表达式是 lengths-list 变量;其值作为整个函数的值返回。

你可以通过通常的方式安装这个函数并尝试它。然后,将光标放在以下表达式之后,键入 C-x C-e (eval-last-sexp).

(lengths-list-file
 "/usr/local/share/emacs/22.1/lisp/emacs-lisp/debug.el")

你可能需要更改文件的路径名;这里的路径是适用于 GNU Emacs 版本 22.1 的。要更改表达式,将其复制到 *scratch* 缓冲区并进行编辑。

此外,为了看到列表的完整长度,而不是截断版本,你可能需要评估以下内容:

(custom-set-variables '(eval-expression-print-length nil))

(See 使用 defcustom 指定变量. 然后评估 lengths-list-file 表达式。)

debug.el 的长度列表在 GNU Emacs 22 中不到一秒钟就产生了,看起来像这样:

(83 113 105 144 289 22 30 97 48 89 25 52 52 88 28 29 77 49 43 290 232 587)

(在我的旧机器上,版本 19 的 debug.el 长度列表花费了七秒钟并且看起来像这样:

(75 41 80 62 20 45 44 68 45 12 34 235)

新版本的 debug.el 包含比早期版本更多的 defuns;而我的新机器比旧机器要快得多。) 请注意,文件中的最后一个定义的长度在列表中是最先的。