Common Lisp 参数传递的几种形式

1. 常规的命名参数:

(defun f (a b)
  (+ a b)) 

(f 1 2)

输出:

3

2. 可选参数:

(defun f (a b &optional c d)
  (list a b c d)) 

(f 1 2 3)

输出:

(1 2 3 NIL)

 

未显示提供的参数,会以 NIL 值代替。

可选参数可以指定默认值:

(defun f (a b &optional (c "Hello") d)
  (list a b c d))

(f 1 2)

输出:

(1 2 "Hello" NIL)

 

可选的默认值也可以通过引用前面的参数值来计算:

(defun make-rectangle (width &optional (height width))
  (list width height)) 

(make-rectangle 3 4)

输出:

(3 4)

(make-rectangle 3)

输出:

(3 3)

 

如果需要判断调用方是否提供了某个可选参数,则可以修改参数列表,在默认值后面添加一个变量名,其值为 t 的时候表示提供了该参数,否则为 NIL.

(defun f (a &optional (b 3 b-supplied-p))
  (list a b b-supplied-p))

输入:

(f 1)

输出:

(1 3 NIL)

输入:

(f 1 2)

输出:

(1 2 T)

3. rest 参数

如果除了命名参数和可选参数之外,还有更多传递的参数值,它们可以被收集到 &rest 符号后面指定的一个列表变量中。

比如,format 和 + 函数的形式可以定义为:

(defun format (stream string &rest values) …)

(defun + (&rest numbers) …)

例如,可以重定义一个 + 函数的形式:

(defun add (&rest values)
  (apply '+ values))

输入:

(add 1 2)

输出:

3

输入:

(add)

输出:

0

 

4. 关键字参数

关键字参数可以用来传递参数列表中任意一部分参数,而不管其所在的位置如何。

可以在命名参数、可选参数,和 rest 参数后面,添加 &key 符号,然后接着指定任意数目的关键字参数。

例子:

(defun f (a &optional b &rest values &key d (e 10) (f 11 f-supplied-p))
  (list a b values d e f)) 

COMMON-LISP-USER>
(f 1) 

(1 NIL NIL NIL 10 11) 

COMMON-LISP-USER>
(f 1 2 :d 99 :e 100) 

(1 2 (:D 99 :E 100) 99 100 11)

另外,还可以给关键字参数指定不同的调用名称和内部名称。这样可以提供比较有意义的 public API 的参数名,而内部实现使用简短的名称。但这个功能不是很常用。

COMMON-LISP-USER>
(defun foo (&key ((:apple a)) ((:box b)) ((:charlie c) 0 c-supplied-p))
  (list a b c c-supplied-p)) 

FOO 

COMMON-LISP-USER>
(foo :apple 10 :box 20 :charlie 30) 

(10 20 30 T)

5. Body 参数

用 &body 符号指定。和 &rest 大致相同,但是具体的 Common Lisp 实现可以扩展其特定的行为。

&body 有 pretty-printing 效果。

6. 组合使用各种形式的参数

如果一个函数需要用到所有类型的参数,那么他们的出现次序必须为:

命名参数,可选参数,rest 参数,关键字参数。

 

如果用到多种类型的参数,最好是把它们进行合适当的合并,或者简化成全部使用关键字参数。否则,可能会出现参数被“吞掉”的情况,从而导致不正确的行为。

posted on 2011-07-25 16:52  NeilChen  阅读(1929)  评论(2编辑  收藏  举报

导航