为什么mapcar只使用从values-list返回的参数之一?

时间:2019-12-12 14:46:53

标签: list lisp common-lisp map-function

我对Lisp很陌生,并且遇到以下问题。

我正在尝试交换多个列表中的数字并生成一个列表列表,结果是每个列表中第一个数字的所有数字都将被收集在一个第一个元素的列表中结果列表;每个列表中的第二个数字将被收集到另一个列表中,该列表是结果列表的第二个元素;等等。

这里是一个例子:

(foo '((3 4 2 4) (2 5 6 9) (1 -2 8 10)) ) 
=> ((3 2 1) (4 5 -2) (2 6 8) (4 9 10))

我当前的解决方案是:

(defun foo (list)
    (mapcar #'list (values-list list))
)

据我了解,values-list应该从给定参数list内部返回所有子列表。然后mapcar将遍历每个子列表的每个元素,并将它们组成一个列表。

这第二部分有效,但是发生的是仅使用了第一个子列表,结果是((3) (4) (2) (4))而不是((3 2 1) (4 5 -2) (2 6 8) (4 9 10))

这是为什么?

(我正在使用CLISP)。

2 个答案:

答案 0 :(得分:3)

在您的代码中

(defun foo (list)
  (mapcar #'list (values-list list)))

values-list将列表作为多个值返回。但是,您正在调用mapcar之类的函数,而普通的函数调用每个参数仅占用一个值。

要修复该问题,您可以通过mapcar致电multiple-value-call

CL-USER 47 > (defun foo (list)
               (multiple-value-call #'mapcar #'list (values-list list)))
FOO

CL-USER 48 > (foo '((3 4 2 4) (2 5 6 9) (1 -2 8 10)) )
((3 2 1) (4 5 -2) (2 6 8) (4 9 10))

然后,以上将所有值传递给mapcar

请注意,参数列表在Common Lisp实现中受到限制。请参阅变量call-arguments-limit。只能确保带有少于call-arguments-limit个参数的函数调用在Common Lisp的给定实现中起作用。

因此,对于列表长度不受限制的列表处理,不应该依赖诸如applymultiple-value-call之类的功能。

使用多个值

Common Lisp支持将多个值作为一种优化。它允许一个人返回多个值,而无需为其构造数据结构。

通常,函数每个参数仅接收单个值。

与多个值有关的所有事情都通过运算符完成:values返回值,multiple-value-bind接收值并将它们绑定到变量,...然后是multiple-value-call,{{1} },values-listmultiple-values-limitmultiple-value-listmultiple-value-setq(setf (values ...) ...)multiple-value-prog1

答案 1 :(得分:2)

您要尝试的是将矩阵转置为列表列表。在Common Lisp中执行此操作的标准方法是使用以下功能:

CL-USER> (defun transpose (list)
           (apply #'mapcar #'list list))
TRANSPOSE
CL-USER> (transpose '((3 4 2 4) (2 5 6 9) (1 -2 8 10)))
((3 2 1) (4 5 -2) (2 6 8) (4 9 10))

values-list的作用是按顺序返回列表中的所有元素作为不同的结果值:

CL-USER> (values-list '((3 4 2 4) (2 5 6 9) (1 -2 8 10)))
(3 4 2 4)
(2 5 6 9)
(1 -2 8 10)

因此,您的函数采用返回的主值(第一个列表)并将list应用于其所有元素。

请注意,transpose函数在应用于'((3 4 2 4) (2 5 6 9) (1 -2 8 10))之类的列表参数时是如何工作的:它在某种意义上“转换”(某种意义上)该调用:

(mapcar #'list '(3 4 2 4) '(2 5 6 9) '(1 -2 8 10))

实际上,如果您直接致电此表格,它将产生正确的结果:

CL-USER> (mapcar #'list '(3 4 2 4) '(2 5 6 9) '(1 -2 8 10)) 
((3 2 1) (4 5 -2) (2 6 8) (4 9 10))