while 循环 ¶接下来是两个 while 循环。第一个 while 的真假测试会在 forward-sentence 的前缀参数为负数时成立,用于实现反向移动。该循环的主体与第二个 while 子句的主体类似,但并不完全相同。我们略过这个 while 循环,重点讲解第二个。
第二个 while 循环用于向前移动光标。其结构如下:
(while (> arg 0) ; true-or-false-test
(let varlist
(if (true-or-false-test)
then-part
else-part
(setq arg (1- arg)))) ; while loop decrementer
该 while 循环属于递减型循环。(See A Loop with a Decrementing Counter。)只要计数器(此处为变量 arg)大于 0,真假测试就成立;并且每次循环重复时,递减器都会将计数器的值减 1。
如果调用 forward-sentence 时未指定前缀参数(这是最常用的方式),该 while 循环会执行一次,因为 arg 的值为 1。
while 循环的主体由一个 let 表达式构成,该表达式创建并绑定一个局部变量,其内部主体为一个 if 表达式。
while 循环的主体如下:
(let ((par-end
(save-excursion (end-of-paragraph-text) (point))))
(if (re-search-forward sentence-end par-end t)
(skip-chars-backward " \t\n")
(goto-char par-end)))
let 表达式创建并绑定局部变量 par-end。稍后我们会看到,该局部变量用于为正则表达式搜索提供边界或限制。如果在段落内未能找到合法的句子结尾,搜索会在到达段落末尾时停止。
首先,我们来看 par-end 如何绑定到段落结尾的值。let 会将 par-end 的值设为 Lisp 解释器对以下表达式求值后返回的结果:
(save-excursion (end-of-paragraph-text) (point))
在该表达式中,(end-of-paragraph-text) 将光标移动到段落结尾,(point) 返回当前光标位置的值,随后 save-excursion 将光标恢复到原始位置。因此,let 将 par-end 绑定到 save-excursion 表达式的返回值,即段落结尾的位置。(end-of-paragraph-text 函数使用了 forward-paragraph,我们稍后会讲解。)
接下来 Emacs 对 let 的主体求值,该主体是一个 if 表达式,如下所示:
(if (re-search-forward sentence-end par-end t) ; if-part (skip-chars-backward " \t\n") ; then-part (goto-char par-end))) ; else-part
if 会测试其第一个参数是否为真,若为真则执行 then 分支;否则 Emacs Lisp 解释器会执行 else 分支。该 if 表达式的真假测试即为正则表达式搜索。
将 forward-sentence 函数的核心逻辑写在这里看起来有些奇怪,但这是 Lisp 中实现此类操作的常用方式。