我们这个简单的示例函数 (lambda (a b c) (+ a b c))
指定了三个参数变量,因此调用时必须传入三个参数:
若尝试仅传入两个或四个参数,会触发 wrong-number-of-arguments 错误
(see 错误)。
编写支持省略特定参数的函数往往会带来便利。例如函数 substring
接受三个参数——一个字符串、起始索引和结束索引——但如果省略第三个参数,
其默认值会设为该字符串的 长度。此外,让某些函数支持接收数量不定的参数
也很实用,就像 list 和 + 函数那样。
若要指定函数调用时可省略的可选参数,只需在这些可选参数前加入关键字 &optional 即可。
若要接收零个或多个额外参数(形成参数列表),则在最后一个参数前加入关键字 &rest。
因此,参数列表的完整语法格式如下:
(required-vars... [&optional [optional-vars...]] [&rest rest-var])
方括号表示 &optional 和 &rest 子句及其后续的变量均为可选内容。
调用函数时,每个 required-vars 都需要对应一个实际参数。
零个或多个 optional-vars 可附带实际参数;除非 lambda 列表使用了 &rest,
否则不允许传入超出该范围的实际参数。若使用了 &rest,则可传入任意数量的额外实际参数。
若省略可选变量和剩余变量对应的实际参数,这些变量会默认取值为 nil。
函数无法区分「显式传入的 nil 参数」和「省略的参数」。
不过函数体可自行将 nil 视为其他有意义值的简写形式。
substring 函数正是如此:向其第三个参数传入 nil,
等价于使用所传入字符串的长度作为该参数值。
通用Lisp说明: 通用Lisp允许函数指定可选参数省略时使用的默认值; 而Emacs Lisp始终将
nil作为默认值。Emacs Lisp不支持通过supplied-p变量判断参数是否被显式传入。
例如,形如下面这样的参数列表:
(a b &optional c d &rest e)
该参数列表会将 a 和 b 绑定到前两个实际参数(这两个参数为必传项)。
若传入更多的一个或两个参数,则 c 和 d 会分别绑定到这些参数;
前四个参数之后的所有参数会被收集为一个列表,e 则绑定到该列表。
因此:若仅传入两个参数,c、d 和 e 的值均为 nil;
若传入两个或三个参数,d 和 e 的值为 nil;
若传入四个及以下参数,e 的值为 nil。
需注意:若恰好传入五个参数,且为 e 显式传入 nil 作为参数,
该 nil 参数会以包含一个元素的列表(即 (nil))形式传递给 e——
这与为 e 传入任何其他单一值的处理方式一致。
必选参数不能出现在可选参数之后——这种写法在逻辑上毫无意义。
要理解为何必须遵循此规则,不妨假设示例中的 c 为可选参数、d 为必选参数:
若传入三个实际参数,第三个参数应绑定到哪个变量?是绑定给 c,还是 d?
两种解读都能找到理由。同理,在 &rest 参数之后也不能再出现任何参数
(无论是必选参数还是可选参数),这同样不符合逻辑。
以下是一些参数列表及合法调用的示例:
(funcall (lambda (n) (1+ n)) ; One required: 1) ; requires exactly one argument. ⇒ 2 (funcall (lambda (n &optional n1) ; One required and one optional: (if n1 (+ n n1) (1+ n))) ; 1 or 2 arguments. 1 2) ⇒ 3 (funcall (lambda (n &rest ns) ; One required and one rest: (+ n (apply '+ ns))) ; 1 or more arguments. 1 2 3 4 5) ⇒ 15