5.5 多值 (Multiple Values)
曾有人这么说,为了要强调函数式编程的重要性,每个 Lisp 表达式都返回一个值。现在事情不是这么简单了;在 Common Lisp 里,一个表达式可以返回零个或多个数值。最多可以返回几个值取决于各家实现,但至少可以返回 19 个值。
多值允许一个函数返回多件事情的计算结果,而不用构造一个特定的结构。举例来说,内置的 get-decoded-time
返回 9 个数值来表示现在的时间:秒,分,时,日期,月,年,天,以及另外两个数值。
多值也使得查询函数可以分辨出 nil
与查询失败的情况。这也是为什么 gethash
返回两个值。因为它使用第二个数值来指出成功还是失败,我们可以在哈希表里储存 nil
,就像我们可以储存别的数值那样。
values
函数返回多个数值。它一个不少地返回你作为数值所传入的实参:
> (values 'a nil (+ 2 4))
A
NIL
6
如果一个 values
表达式,是函数主体最后求值的表达式,它所返回的数值变成函数的返回值。多值可以原封不地通过任何数量的返回来传递:
> ((lambda () ((lambda () (values 1 2)))))
1
2
然而若只预期一个返回值时,第一个之外的值会被舍弃:
> (let ((x (values 1 2)))
x)
1
通过不带实参使用 values
,是可能不返回值的。在这个情况下,预期一个返回值的话,会获得 nil
:
> (values)
> (let ((x (values)))
x)
NIL
要接收多个数值,我们使用 multiple-value-bind
:
> (multiple-value-bind (x y z) (values 1 2 3)
(list x y z))
(1 2 3)
> (multiple-value-bind (x y z) (values 1 2)
(list x y z))
(1 2 NIL)
如果变量的数量大于数值的数量,剩余的变量会是 nil
。如果数值的数量大于变量的数量,多余的值会被舍弃。所以只想印出时间我们可以这么写:
> (multiple-value-bind (s m h) (get-decoded-time)
(format t "~A:~A:~A" h m s))
"4:32:13"
你可以借由 multiple-value-call
将多值作为实参传给第二个函数:
> (multiple-value-call #'+ (values 1 2 3))
6
还有一个函数是 multiple-value-list
:
> (multiple-value-list (values 'a 'b 'c))
(A B C)
看起来像是使用 #'list
作为第一个参数的来调用 multiple-value-call
。