14.6 lengths-list-file 详解

lengths-list-file 函数的核心是一个 while 循环,其中包含逐个 defun 前移光标的 函数,以及统计每个 defun 中单词与符号数量的函数。该核心外围需要包裹完成其他任务的函数, 包括查找文件、确保光标从文件开头开始。函数定义如下:

(defun lengths-list-file (filename)
  "Return list of definitions' lengths within FILE.
The returned list is a list of numbers.
Each number 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)))

该函数接收一个参数,即要处理的文件名。它有四行文档,但没有交互式声明。由于人们在看不到 任何进展时会担心程序出错,函数体第一行是一条提示消息。

下一行包含 save-excursion,在函数结束时将 Emacs 注意力切回当前缓冲区。 这在将该函数嵌入其他假定光标恢复到原缓冲区的函数中时十分有用。

let 表达式的变量列表中,Emacs 查找文件并将局部变量 buffer 绑定到 存放该文件的缓冲区。同时,Emacs 创建局部变量 lengths-list

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

下一行中,Emacs 将缓冲区设为只读。理想情况下这一行并非必需,统计函数定义中单词与符号 的所有函数都不应修改缓冲区。况且,即使缓冲区被修改,也不会被保存。这一行完全是出于 高度(或许过度)的谨慎。谨慎的原因是该函数及其调用的函数操作 Emacs 源码,意外修改 会带来不便。不用说,我也是在一次实验出错并开始修改我的 Emacs 源码文件后,才意识到 需要这一行…

接下来调用 widen 函数,如果缓冲区被 narrowed 则展开。该函数通常并非必需——Emacs 在缓冲区不存在时会创建新缓冲区;但如果访问该文件的缓冲区已存在,Emacs 会直接返回它。 这种情况下缓冲区可能被 narrowed,必须展开。如果我们想做到完全友好,应该保存 narrowing 状态与光标位置,但这里不做处理。

(goto-char (point-min)) 表达式将光标移至缓冲区开头。

然后是 while 循环,函数的主要工作在此完成。循环中,Emacs 计算每个定义的长度, 并构造包含该信息的长度列表。

Emacs 在处理完缓冲区后将其杀死,以节省 Emacs 内部空间。我的 GNU Emacs 19 版本有 300 多个相关源码文件;GNU Emacs 22 则有超过一千个源码文件。另一个函数会对每个文件 调用 lengths-list-file

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

你可以按常规方式安装该函数进行测试。然后将光标置于以下表达式后,输入 C-x C-eeval-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 Specifying Variables using 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 包含更多 defun;并且我的新机器比旧机器快得多。)

注意,文件中最后一个定义的长度出现在列表首位。