5.3.1 可选参数

除非另有说明,Lisp 期望具有函数定义中参数的函数在调用时将为该参数传递一个值。如果没有这样做,将出现错误并显示消息 ‘Wrong number of arguments’。

然而,可选参数是Lisp的一个特性:使用特定的关键字告诉Lisp解释器该参数是可选的。关键字是 &optional。 (‘&’ 在 ‘optional’ 前面是关键字的一部分。)在函数定义中,如果一个参数跟在关键字 &optional 后面,那么在调用函数时无需传递值给该参数。

因此,beginning-of-buffer 函数的函数定义的第一行看起来像这样:

(defun beginning-of-buffer (&optional arg)

概括而言,整个函数看起来像这样:

(defun beginning-of-buffer (&optional arg)
  "documentation…"
  (interactive "P")
  (or (is-the-argument-a-cons-cell arg)
      (and are-both-transient-mark-mode-and-mark-active-true)
      (push-mark))
  (let (determine-size-and-set-it)
  (goto-char
    (if-there-is-an-argument
        figure-out-where-to-go
      else-go-to
      (point-min))))
   do-nicety

该函数类似于 simplified-beginning-of-buffer 函数,只是 interactive 表达式的参数是 "P",并且 goto-char 函数后面跟着一个 if-then-else 表达式,用于确定如果有一个不是 cons cell 的参数,光标应该放在哪里。

(由于我在很多章节里都没有解释 cons cell,请考虑忽略函数 consp。See 如何实现列表, 以及 Cons Cell and List Types in The GNU Emacs Lisp Reference Manual.)

interactive 表达式中的 "P" 告诉 Emacs 将前缀参数(如果有的话)以原始形式传递给函数。前缀参数是通过按 META 键后面跟一个数字,或者按 C-u 然后是一个数字来生成的。(如果不输入数字,C-u 默认为带有 4 的 cons cell。在 interactive 表达式中的小写 "p" 会导致函数将前缀参数转换为数字。)

if 表达式的真值测试看起来复杂,但实际上并不复杂:它检查 arg 是否具有非 nil 的值,以及它是否是 cons cell。 (这就是 consp 做的事情;它检查其参数是否为 cons cell。)如果 arg 具有非 nil 的值(并且不是 cons cell),这个真值测试将返回 true,并且 if 表达式的 then-部分将被评估。另一方面,如果 beginning-of-buffer 没有带参数调用,arg 的值将为 nil,并且 if 表达式的 else-部分将被评估。else-部分就是 point-min,当这是结果时,整个 goto-char 表达式是 (goto-char (point-min)),这就是我们在简化形式中看到的 beginning-of-buffer 函数。