在Common Lisp中转置列表

Pau*_*han 11 lisp common-lisp

我试图转置列表清单; 我的评论表明了思考过程.

(setq thingie  '((1 2 3) (4 5 6) (7 8 9)))  ;;test case

(defun trans (mat)
  (if (car mat)
    (let ((top (mapcar 'car  mat))   ;;slice the first row off as a list
          (bottom (mapcar 'cdr mat))) ;;take the rest of the rows
      (cons top (trans bottom))))    ;;cons the first-row-list with the next-row-list
   mat)

(trans thingie)
=> ((1 2 3) (4 5 6) (7 8 9))           ;;wait what? 
Run Code Online (Sandbox Code Playgroud)

但是,我真的希望它成为

((1 4 7) (2 5 8) (3 6 9))
Run Code Online (Sandbox Code Playgroud)

我究竟做错了什么?

Sva*_*nte 25

有一个简单的方法:

(defun rotate (list-of-lists)
  (apply #'mapcar #'list list-of-lists))
Run Code Online (Sandbox Code Playgroud)

您的尝试总是返回原件mat.修复缩进,您会看到if表单中返回的值总是被丢弃.

编辑: 这是如何工作的:

  • List获取任意数量的参数并列出它.它的功能定义可以这样设想:

    (defun list (&rest arguments)
      arguments) ; exploit the automatic &rest construction
    
    Run Code Online (Sandbox Code Playgroud)
  • Mapcar获取一个函数和任意数量的列表,然后通过使用这些列表中的一个元素调用该函数来创建一个新的值列表.示例:(mapcar #'foo '((A B) (C D)))将构造一个新列表,其中第一个元素是结果,(foo 'A 'C)第二个元素是结果(foo 'B 'D).

  • Apply将可扩展参数列表指示符作为其最后一个参数.这意味着如果你给它一个列表作为它的最后一个参数,那么该列表可以"传播"以产生该函数的各个参数.示例:(apply #'+ '(1 2 3))具有相同的效果(+ 1 2 3).

现在您可以扩展该行:

(apply #'mapcar #'list '((A B) (C D)))
Run Code Online (Sandbox Code Playgroud)

=>

(mapcar #'list '(A B) '(C D))
Run Code Online (Sandbox Code Playgroud)

=>

(list (list 'A 'C) (list 'B 'D))
Run Code Online (Sandbox Code Playgroud)

=>

'((A C) (B D))
Run Code Online (Sandbox Code Playgroud)