каковы «минусы» добавления элемента в конец списка?

каков обычный способ добавить элемент в конец списка?

У меня есть список (1 2 3) и я хочу добавить к нему 4 (где 4 — результат оценки (+ 2 2))

(setf nlist '(1 2 3))  
(append nlist (+ 2 2))  

Это говорит о том, что append ожидает список, а не число. Как бы я это сделал?


person r b    schedule 22.06.2011    source источник


Ответы (9)


Вы можете использовать append, но имейте в виду, что это может привести к снижению производительности, если используется в цикле или в очень длинных списках.

(append '(1 2 3) (list (+ 2 2)))

Если важна производительность, обычная идиома заключается в создании списков путем добавления в начало (используя cons), затем обратное (или nreverse).

person danlei    schedule 22.06.2011
comment
Обычно консинг и реверс в порядке. Если у вас много длинных списков и реверсирование становится проблемой производительности, используйте другую структуру данных или оставьте указатель на конец списка вручную. (В других ответах есть несколько предложений.) - person danlei; 30.03.2017

Если идиома «против» в начале, «закончить путем реверсирования» вам не подходит (если вам, например, нужно передать список другим функциям ВО ВРЕМЯ его построения), есть также трюк с отслеживанием конца. Тем не менее, вероятно, будет чище просто построить список, переместив его в начало, а затем закончить, используя reverse или nreverse, прежде чем, наконец, использовать его.

По сути, это позволяет вам иметь список в правильном порядке при его создании за счет необходимости отслеживать его.

(defun track-tail (count)
  (let* ((list (cons 0 nil))
         (tail list))
    (loop for n from 1 below count
       do (progn
        (setf (cdr tail) (cons n nil))
        (setf tail (cdr tail))
        (format t "With n == ~d, the list is ~a~%" n list)))
    list))

Это дает следующий результат:

CL-USER> (track-tail 5)
With n == 1, the list is (0 1)
With n == 2, the list is (0 1 2)
With n == 3, the list is (0 1 2 3)
With n == 4, the list is (0 1 2 3 4)
(0 1 2 3 4)
person Vatine    schedule 26.06.2011
comment
Я думаю, что этот метод намного предпочтительнее обратного построения и реверсирования. Мы также можем предоставить специальный тип данных отслеживания (cons list last_cons_cell) и специальные функции, использующие его для добавления в конце, например (defun eadd (elt ls) (rplacd (cdr ls) (list elt)) (rplacd ls (cddr ls)) ls) и т. д. - person Will Ness; 21.07.2013

Вы не указали тип Lisp, поэтому, если вы используете Emacs Lisp и библиотеку dash для работы со списками , у него есть функция -snoc, которая возвращает новый список с элементом, добавленным в конец. Название перевернуто "минусы".

(-snoc '(1 2) 3) ; (1 2 3)
person Mirzhan Irkegulov    schedule 06.08.2014

Вы также можете использовать nconc для создания списка, похожего на append, только он изменяет структуру входных списков.

(nconc nlist (list (+ 2 2)))
person Trey Jackson    schedule 22.06.2011
comment
В Common Lisp используйте NCONC только в том случае, если nlist является конструкционным списком, а не литеральными данными. - person Rainer Joswig; 16.04.2012
comment
Наверное, лучше вообще не предлагать NCONC новичку. - person finnw; 13.11.2012
comment
НИКОГДА, НИКОГДА не используйте NCONC. Его побочный эффект укусит вас за задницу. И это даже асимптотически не менее сложно, чем APPEND. - person Faré; 05.09.2015
comment
@Faré Никогда, никогда не делайте абсолютных заявлений. - person Trey Jackson; 05.09.2015
comment
Никогда, никогда не делайте абсолютных утверждений. Красиво и рекурсивно. - person gsl; 18.10.2020

Эта функция может быть полезна в некоторых ситуациях, она прозрачно добавляет один элемент в список, т. е. изменяет список, но возвращает добавленный элемент (заключенный в список):

(defun attach1 (lst x)
  (setf (cdr (last lst)) (cons x nil)))

;; (attach1 nlist (+ 2 2)) ; append without wrapping element to be added in a list
person mmj    schedule 16.04.2012
comment
Это похоже на NCONC. (nconc lst (список x)). - person Rainer Joswig; 16.04.2012
comment
Нет, это отличается от NCONC; с этой функцией ATTACH1 вам не нужно (и вы не должны) заключать элемент, который будет добавлен в список. - person mmj; 16.04.2012
comment
(setf (cdr (последний список)) (cons x nil)) = (nconc lst (cons x nil)) = (nconc lst (список x)) - person Rainer Joswig; 16.04.2012
comment
У нас разные представления о «похожем». Для меня две функции, которым нужны разные типы аргументов (атом и список), не похожи, тогда как для вас они одинаковы. В любом случае, независимо от включения в список элемента для добавления, я согласен с вами. - person mmj; 17.04.2012

Cons-ing в конце списка может быть достигнуто с помощью этой функции:

(defun cons-last (lst x)
  (let ((y (copy-list lst))) (setf (cdr (last y)) (cons x nil)) y))

;; (cons-last nlist (+ 2 2))
person mmj    schedule 16.04.2012
comment
у этого есть недостаток - обход списка дважды - сначала с copy-list, второй с last. Лучше изменить track-trail из ответа Ватина. - person Will Ness; 14.11.2012

Если вы пытаетесь добавить два списка, например (1 2 3) + (1 2 3), вот код (рекурсивный)

(defun add-to-all (x y)
    (T (appendl (+ (first x) (first y)) (add-to-all (tail x) (tail y)) ))
)

Если вы пытаетесь добавить элемент в конец второго списка, например 3 + (1 2 3)

(defun add-to-all (x y)
  (cond ((null? y) nil)
    (T (appendl (+ (first x) (first y)) (add-to-all (tail x) (tail y)) ))
  )
)
person Giri    schedule 04.12.2014

(добавьте l (список e)) ; e — это элемент, который вы хотите добавить в конец списка

person Nadhem Jemmali    schedule 14.03.2017

Если вы хотите добавить элемент в конец заданного списка, не изменяя этот список, то, как предлагалось ранее, вы можете использовать такую ​​функцию, как

(defun annex (lst item)
  "Returns a new list with item added onto the end of the given list."
  (nconc (copy-list lst) (list item)))

Это возвращает новый расширенный список, сохраняя при этом список ввода. Однако, если вы хотите изменить список ввода, чтобы включить добавленный элемент, вы можете использовать макрос, например

(define-modify-macro pushend (item)
  (lambda (place item)
    (nconc place (list item)))
  "Push item onto end of a list: (pushend place item).")

Pushend работает как push, но «толкает» элемент в конец заданного списка. Также обратите внимание, что порядок аргументов обратный push.

person davypough    schedule 12.06.2018