what is the 'cons' to add an item to the end of the list?
Asked Answered
M

9

17

what's the typical way to add an item to the end of the list?

I have a list (1 2 3) and want to add 4 to it (where 4 is the result of an evaluation (+ 2 2))

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

This says that append expects a list, not a number. How would I accomplish this?

Millur answered 22/6, 2011 at 12:39 Comment(0)
C
30

You could use append, but beware that it can lead to bad performance if used in a loop or on very long lists.

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

If performance is important, the usual idiom is building lists by prepending (using cons), then reverse (or nreverse).

Catatonia answered 22/6, 2011 at 13:18 Comment(1)
Usually, consing and reversing is fine. If you have many long lists and the reversing becomes a performance problem, use a different data structure or keep a pointer to the end of the list manually. (There are a few suggestions in the other answers.)Catatonia
M
8

If the "cons at the front, finish by reversing" idiom isn't suitable for you (if you. for example, need to pass the list on to other functions DURING its construction), there's also the "keep track of the end" trick. However, it's probably cleaner to just build the list by consing to the front of it, then finish by using reverse or nreverse before finally using it.

In essence, this allows you to have the list in the right order while building it, at the expense of needing to keep track of it.

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

This gives the following output:

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)
Molini answered 26/6, 2011 at 6:59 Comment(1)
I think this technique is much preferable to reverse building and reversing. We can also provide a special tracking data type (cons list last_cons_cell) and special functions using it for addition at end like (defun eadd (elt ls) (rplacd (cdr ls) (list elt)) (rplacd ls (cddr ls)) ls) etc.Jumbo
F
5

You can also use nconc to create the list, which is like append, only it modifies the structure of the input lists.

(nconc nlist (list (+ 2 2)))
Fetid answered 22/6, 2011 at 15:28 Comment(5)
In Common Lisp use NCONC only if nlist is a consed list, not literal data.Kato
Probably better not to suggest NCONC to a beginner at all.Re
NEVER, EVER use NCONC. Its side-effect will come bite you in the ass. And it's not even asymptotically less complex than APPEND.Reinaldo
@Faré Never, ever make absolute statements.Fetid
"Never, ever make absolute statements." Nice and recursive.Woolsack
W
5

You haven't specified the kind of Lisp, so if you use Emacs Lisp and dash list manipulation library, it has a function -snoc that returns a new list with the element added to the end. The name is reversed "cons".

(-snoc '(1 2) 3) ; (1 2 3)
Wroughtup answered 6/8, 2014 at 9:48 Comment(0)
C
3

This function might be useful in some situations, it transparently appends a single element to a list, i.e. it modifies the list but returns the appended element (enclosed in a list):

(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
Cosy answered 16/4, 2012 at 11:26 Comment(4)
That's similar to NCONC. (nconc lst (list x)).Kato
No it's different from NCONC; with this ATTACH1 function you don't need to (and you must not) enclose the element to be added in a list.Cosy
(setf (cdr (last lst)) (cons x nil)) = (nconc lst (cons x nil)) = (nconc lst (list x))Kato
We have different ideas of 'similar'. For me two functions that need different type of arguments (atom vs. list) are not similar, whereas they are for you. Anyway, regardless of enclosing in a list the element to append, I agree with you.Cosy
F
2

(append l (list e)) ; e is the element that you want to add at the tail of a list

Fireboat answered 14/3, 2017 at 18:22 Comment(0)
C
1

Cons-ing at the end of a list can be achieved with this function:

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

;; (cons-last nlist (+ 2 2))
Cosy answered 16/4, 2012 at 13:5 Comment(1)
this has a drawback of traversing a list twice - first with copy-list, second with last. Better to modify the track-trail from Vatine's answer.Jumbo
P
1

If you are trying to add two lists for example (1 2 3) + (1 2 3) here is the code (recursive)

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

If you are trying to add an item to the end of the second list, for example 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)) ))
  )
)
Peltry answered 4/12, 2014 at 17:55 Comment(0)
H
-1

If you want to add an item onto the end of a given list without changing that list, then as previously suggested you can use a function like

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

This returns a new extended list, while preserving the input list. However, if you want to modify the input list to include the added item, then you can use a macro like

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

Pushend operates like push, but "pushes" the item onto the end of the given list. Also note the argument order is the reverse of push.

Hebraize answered 12/6, 2018 at 7:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.