【问题标题】:Why is mapcar only using one of the arguments returned from values-list?为什么 mapcar 只使用从 values-list 返回的参数之一?
【发布时间】:2019-12-12 14:46:53
【问题描述】:

我是 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)。

【问题讨论】:

  • 搜索“值列表 CLHS”。
  • 在这种情况下,实现并没有什么不同,因为您所询问的行为在不同的实现中得到了很好的标准化。

标签: list lisp common-lisp map-function


【解决方案1】:

在您的代码中

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

values-list 将列表作为多个值返回。但是您正在调用像 mapcar 这样的函数,而正常的函数调用每个参数只接受一个值。

要修复它,您可以通过multiple-value-call 致电mapcar:

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 实现中有效。

因此,对于具有无限长度列表的列表处理,不应依赖于 apply 或 multiple-value-call 等特征。

使用多个值

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

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

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

【讨论】:

  • 谢谢!我不认为值会与普通参数区别对待,所以我认为 mapcar 只会将每个值作为参数。
【解决方案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))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-07
    • 2019-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多