在其他操作之外,当剪切环非空且 do-not-move 值为 nil 时,if 表达式的 else 分支会将 kill-ring-yank-pointer 的值设为 ARGth-kill-element。
相关代码如下:
(nthcdr (mod (- n (length kill-ring-yank-pointer))
(length kill-ring))
kill-ring)))
这段代码需要仔细分析。除非设定不移动指针,否则 current-kill 函数会修改 kill-ring-yank-pointer 的指向,这正是表达式 (setq kill-ring-yank-pointer ARGth-kill-element)) 的作用。同时很明显,ARGth-kill-element 被赋值为剪切环的某个 CDR 部分,使用了前文介绍过的 nthcdr 函数。(See copy-region-as-kill。)具体是如何实现的呢?
如我们之前所见(see nthcdr),nthcdr 函数通过反复取列表的 CDR 工作 — 持续取 CDR 的 CDR 的 CDR…
下面两个表达式效果完全相同:
(setq kill-ring-yank-pointer (cdr kill-ring)) (setq kill-ring-yank-pointer (nthcdr 1 kill-ring))
不过此处的 nthcdr 表达式更为复杂,它使用 mod 函数确定要选取哪一个 CDR。
(你应当记得先查看内层函数;事实上我们需要深入 mod 内部。)
mod 函数返回第一个参数对第二个参数取模的结果,即第一个参数除以第二个参数后的余数,返回值符号与第二个参数一致。
例如:
(mod 12 4)
⇒ 0 ;; because there is no remainder
(mod 13 4)
⇒ 1
本例中第一个参数通常小于第二个,这完全合法。 That is fine.
(mod 0 4) ⇒ 0 (mod 1 4) ⇒ 1
我们可以猜到 - 函数的作用:它与 + 类似,只是执行减法而非加法,- 函数用第一个参数减去第二个参数。同时我们已经知道 length 函数的作用(see 获取列表长度:length),它返回列表的长度。
而 n 是 current-kill 函数的必选参数名。
因此当 nthcdr 的第一个参数为零时,nthcdr 表达式会返回整个列表,执行下面代码即可验证:
;; kill-ring-yank-pointer and kill-ring have a length of four ;; and (mod (- 0 4) 4) ⇒ 0 (nthcdr (mod (- 0 4) 4) '("fourth line of text" "third line" "second piece of text" "first some text"))
当 current-kill 函数的第一个参数为 1 时,nthcdr 表达式会返回去掉首个元素后的列表。
(nthcdr (mod (- 1 4) 4)
'("fourth line of text"
"third line"
"second piece of text"
"first some text"))
顺带一提,kill-ring 与 kill-ring-yank-pointer 均为 全局变量(global variables)。这意味着 Emacs Lisp 中的任意表达式都可以访问它们,不同于 let 定义的局部变量或参数列表中的符号。局部变量仅能在定义它们的 let 表达式、或在参数列表中声明它们的函数内部(以及其调用的表达式内)访问。
(See let Prevents Confusion, 和
The defun Macro.)