Транспонирование списков в Common Lisp - PullRequest
11 голосов
/ 18 августа 2010

Я пытаюсь транспонировать список списков;мои комментарии указывают на мыслительный процесс.

(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? 

Но я действительно хочу, чтобы он был

((1 4 7) (2 5 8) (3 6 9))

Что я делаю не так?

1 Ответ

25 голосов
/ 18 августа 2010

Для этого есть простой способ:

(defun rotate (list-of-lists)
  (apply #'mapcar #'list list-of-lists))

Ваша попытка всегда возвращает оригинал mat. Исправьте отступ, и вы увидите, что возвращаемое значение из формы if всегда выбрасывается.

Редактировать: Как это работает:

  • List принимает любое количество аргументов и составляет их список. Его определение функции можно представить примерно так:

    (defun list (&rest arguments)
      arguments) ; exploit the automatic &rest construction
    
  • 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)))

=>

(mapcar #'list '(A B) '(C D))

=>

(list (list 'A 'C) (list 'B 'D))

=>

'((A C) (B D))
...