5.5 多值 (Multiple Values)

曾有人这么说,为了要强调函数式编程的重要性,每个 Lisp 表达式都返回一个值。现在事情不是这么简单了;在 Common Lisp 里,一个表达式可以返回零个或多个数值。最多可以返回几个值取决于各家实现,但至少可以返回 19 个值。

多值允许一个函数返回多件事情的计算结果,而不用构造一个特定的结构。举例来说,内置的 get-decoded-time 返回 9 个数值来表示现在的时间:秒,分,时,日期,月,年,天,以及另外两个数值。

多值也使得查询函数可以分辨出 nil 与查询失败的情况。这也是为什么 gethash 返回两个值。因为它使用第二个数值来指出成功还是失败,我们可以在哈希表里储存 nil ,就像我们可以储存别的数值那样。

values 函数返回多个数值。它一个不少地返回你作为数值所传入的实参:

  1. > (values 'a nil (+ 2 4))
  2. A
  3. NIL
  4. 6

如果一个 values 表达式,是函数主体最后求值的表达式,它所返回的数值变成函数的返回值。多值可以原封不地通过任何数量的返回来传递:

  1. > ((lambda () ((lambda () (values 1 2)))))
  2. 1
  3. 2

然而若只预期一个返回值时,第一个之外的值会被舍弃:

  1. > (let ((x (values 1 2)))
  2. x)
  3. 1

通过不带实参使用 values ,是可能不返回值的。在这个情况下,预期一个返回值的话,会获得 nil :

  1. > (values)
  2. > (let ((x (values)))
  3. x)
  4. NIL

要接收多个数值,我们使用 multiple-value-bind :

  1. > (multiple-value-bind (x y z) (values 1 2 3)
  2. (list x y z))
  3. (1 2 3)
  4. > (multiple-value-bind (x y z) (values 1 2)
  5. (list x y z))
  6. (1 2 NIL)

如果变量的数量大于数值的数量,剩余的变量会是 nil 。如果数值的数量大于变量的数量,多余的值会被舍弃。所以只想印出时间我们可以这么写:

  1. > (multiple-value-bind (s m h) (get-decoded-time)
  2. (format t "~A:~A:~A" h m s))
  3. "4:32:13"

你可以借由 multiple-value-call 将多值作为实参传给第二个函数:

  1. > (multiple-value-call #'+ (values 1 2 3))
  2. 6

还有一个函数是 multiple-value-list :

  1. > (multiple-value-list (values 'a 'b 'c))
  2. (A B C)

看起来像是使用 #'list 作为第一个参数的来调用 multiple-value-call