5.3.1 可选参数

默认情况下,Lisp 会认为函数定义中声明的参数,在调用时必须传入对应的值。如果没有传入,就会报错并提示 ‘参数数量错误(Wrong number of arguments)’。

不过 Lisp 支持可选参数:使用特定关键字(keyword)告知 Lisp 解释器某个参数是可选的。该关键字是 &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 表达式,用于在传入非序对参数时计算光标位置。

(序对相关内容要在后面章节才会讲解,暂时可以忽略 consp 函数。参见 See How Lists are Implemented,以及 Cons Cell and List Types in The GNU Emacs Lisp Reference Manual。)

interactive 表达式中的 "P" 告诉 Emacs:如果存在前缀参数,以原始形式传给函数。前缀参数通过按 META 加数字,或 C-u 加数字生成。(如果不输入数字,C-u 默认生成一个值为 4 的序对。如果 interactive 中使用小写 "p",函数会自动将前缀参数转为数字。)

这个 if 表达式的判断条件看起来复杂,其实只是检查 arg 是否为非 nil 且不是序对。(consp 的作用就是判断参数是否为序对。)如果 beginning-of-buffer 带数字参数调用,判断条件为真,执行 then 部分;如果不带参数,argnil,执行 else 部分。else 部分就是 point-min,此时 goto-char 等价于 (goto-char (point-min)),也就是简化版 beginning-of-buffer 的行为。