确定目标元素

在其他操作之外,当剪切环非空且 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 工作 — 持续取 CDRCDRCDR

下面两个表达式效果完全相同:

(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),它返回列表的长度。

ncurrent-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-ringkill-ring-yank-pointer 均为 全局变量(global variables)。这意味着 Emacs Lisp 中的任意表达式都可以访问它们,不同于 let 定义的局部变量或参数列表中的符号。局部变量仅能在定义它们的 let 表达式、或在参数列表中声明它们的函数内部(以及其调用的表达式内)访问。

(See let Prevents Confusion, 和 The defun Macro.)