Как сохранить скобки элементов в LISP при использовании функции 'append'? - PullRequest
1 голос
/ 28 сентября 2019

Я хочу создать функцию grouper (L1 L2) , которая имеет два списка в качестве аргументов.Эти два списка состоят из таких элементов, как «(1 2 3)» и «(4 5 6)».Функция grouper возвращает такой список: ((1 4) (2 5) (3 6)).

Вот мой код:

    (defun grouper1 (l1 l2)
      (setq l (list (car l1) (car l2)))   ;;; initialization of symbol 'l'
      (loop
        (setq l1 (cdr l1))
        (when (null (car l1)) (return l))
        (setq l2 (cdr l2))
        (setq l (append l (list (car l1) (car l2))))
      )
    )

Мне нравитсячто я упоминал ранее, я ожидаю, что список вывода выглядит так: ((1 4) (2 5) (3 6)).Но то, что я на самом деле получаю, это (1 4 2 5 3 6).Я не очень понимаю, где эти скобки и как их можно получить в выводе.

Ответы [ 2 ]

3 голосов
/ 29 сентября 2019

Использовать стандартное форматирование.См., Например, Practical Common Lisp .

(defun grouper1 (l1 l2)
  (setq l (list (car l1) (car l2)))   ; initialization of symbol 'l'
  (loop
    (setq l1 (cdr l1))
    (when (null (car l1)) (return l))
    (setq l2 (cdr l2))
    (setq l (append l (list (car l1) (car l2))))))

Вопреки тому, что вы, похоже, подразумеваете под своим комментарием, setq не указано для инициализации переменной.Используйте let, чтобы ввести локальные переменные.

(defun grouper1 (l1 l2)
  (let ((l (list (car l1) (car l2))))
    (loop
      (setf l1 (cdr l1))
      (when (null (car l1)) (return l))
      (setf l2 (cdr l2))
      (setf l (append l (list (car l1) (car l2)))))))))

Ваша проблема в том, что вы append частичные списки, т.е. составляете один список из них.(append (list 1 2) (list 3 4)) - это (1 2 3 4).Вместо этого вы должны собрать их.Другая проблема с append заключается в том, что он неэффективен в цикле, потому что вы создаете квадратичное время выполнения для по существу линейной операции.Полезная идиома, когда вы делаете это «вручную», это использование push и nreverse.Наконец, вы всегда создаете первую пару, даже если первый список ввода был пустым.Вы должны собирать данные только в цикле, а не во время инициализации.

(defun grouper1 (l1 l2)
  (let ((l ()))
    (loop
      (when (endp l1)
        (return (nreverse l)))
      (push (list (car l1) (car l2)) l)
      (setf l1 (cdr l1)
            l2 (cdr l2)))))

Что должно произойти, если l2 пусто?Если он также должен завершиться, то вы также можете использовать расширенную конструкцию цикла, чтобы помочь с управлением списком:

(defun grouper1 (l1 l2)
  (loop :for x1 :in l1
        :and x2 :in l2
        :collect (list x1 x2)))

Или вы можете просто использовать mapcar:

(defun grouper1 (l1 l2)
  (mapcar #'list l1 l2))

Если вы на самом деле хотите завершить работу только тогда, когда l1 подходит к концу, и набрать nil s, когда l2 короче, вам нужно добавить заполнение для этих двух последних решений.

0 голосов
/ 30 сентября 2019

Если собрать nil из более короткого списка и собрать все элементы более длинного списка (хвостовая рекурсивная форма):

(defun grouper2 (l1 l2 &optional (acc nil))
  "Group elements of l1 and l2, continue till end of longer list and 
collect `nil` from shorter."
  (cond ((and (null l1) (null l2)) (nreverse acc))
        (t (grouper2 (cdr l1) (cdr l2) (cons (list (car l1) (car l2)) acc)))))

И вариационная версия (для любого количества списков, как вы хотите):

(defun grouper3 (&rest lists)
  "Group any number of lists collecting all elements from the longest list 
and collecting `nil` from shorter lists."
  (let (result)
    (labels ((grouper-helper (&rest lists)
               (cond ((funcall #'every #'identity (mapcar #'null lists))
                      (nreverse result))
                     (t
                      (setf result (cons (mapcar #'car lists) result))
                      (apply #'grouper-helper (mapcar #'cdr lists))))))
      (apply #'grouper-helper lists))))
;; I used local variable `result` to store what I actually would store in an 
;; optional variable `acc`, due to the reason that `&rest` and `&key` interfer
;; in capturing the arguments...

Вариационная версия, в которой самый короткий список определяет длину возвращаемого списка (более длинные списки усекаются):

(defun grouper4 (&rest lists)
  "Group any number of lists collecting all elements from shortest list."
  (let (result)
    (labels ((grouper-helper (&rest lists)
               (cond ((funcall #'some #'identity (mapcar #'null lists))
                      (nreverse result))
                     (t
                      (setf result (cons (mapcar #'car lists) result))
                      (apply #'grouper-helper (mapcar #'cdr lists))))))
      (apply #'grouper-helper lists))))

Или используя одно из определений @ Svante для grouper1:

;; you need flatten
;; either alexandria:flatten or:

(defun flatten (l)
  (cond ((null l) nil)
        ((atom (car l)) (cons (car l) (flatten (cdr l))))
        (t (append (flatten (car l)) (flatten (cdr l))))))

;; or tailrecursive:
(defun flatten (l &key (acc nil))
  (cond ((null l) (nreverse acc))
        ((atom (car l)) (flatten (cdr l) :acc (cons (car l) acc)))
        (t (flatten (cdr l) :acc (append (reverse (flatten (car l))) acc)))))


(defun grouper4 (&rest lists)
  (mapcar #'flatten (reduce #'grouper1 lists)))

Может быть короче, но макрос (не функция):

(defmacro grouper4 (&rest lists)
  `(mapcar #'list ,@lists))
...