In common-lisp, how can I insert an element into a list in-place?
Asked Answered
S

2

8

Two questions:

1.I want a function to insert an element inside a list in-place (in any position but the start of the list, see question 2 for the reason) such that:

CL> (defun insert-in-place (the-list after-position new-element) .... ) => ...
CL> (setf testy-list (list 'a 'b 'c 'd)) => ...  
CL> testy-list => ('A 'B 'C 'D)  
CL> (insert-in-place testy-list 1 'BOOOO) => ...  
CL> testy-list => ('A 'B 'BOOOO 'C 'D)

2.I think that inserting an element into the start of the list in-place is impossible through a function because the args are passed by value, so since the first cons cell of the list is passed, it is passed by value and it is a copy and so changing its car only changes a copy car, not the original, although the following cons cells are shared and change in place is possible. Am I correct?

Switch answered 8/12, 2010 at 12:46 Comment(0)
N
18

1) here it is:

(defun insert-after (lst index newelt)
  (push newelt (cdr (nthcdr index lst))) 
  lst)
(insert-after '(a c d) 0 'b)   => (A B C D)

2) destructive modification of cons cells:

(setf testy-list '(a bar))
(defun modify (list)
  (setf (first list) 'foo))
(modify testy-list)
testy-list   => (FOO BAR)

This sets the car of the first cons cell to 'foo.

Nonintervention answered 8/12, 2010 at 13:56 Comment(3)
Thanks. About question 2: When you call (modify test-list) what exactly is being passed to modify? The 1st cons cell of test-list as a value or its reference? I mean you answered that I am not correct but I cant find the flaw in my argument about question 2...Switch
Also I ended up doing this on my own: (rplacd (nthcdr position lst) (cons elem (nthcdr (+ 1 position) lst)))) but yours is better. Actually I wanted to setf the nthcdr, but in clisp that I use, nthcdr is not setfable. I wonder if it is worth to make it setfable. See my other question: #4388467Switch
About question 2: when you call (modify testy-list), you pass a cons cell itself ("by reference").Nonintervention
D
3

I made this for a project of mine, handles index 0 and if index is greater than length of list, the new item is appended at end of list. Be aware that it creates a new list so it may not applicable for you. I include it hoping it would be useful for someone.

(defun list-insert-at (lst index new-value)
  (let ((retval nil))
    (loop for i from 0 to (- (length lst) 1) do
      (when (= i index)
        (push new-value retval))
      (push (nth i lst) retval))
    (when (>= index (length lst))
      (push new-value retval))
    (nreverse retval)))

CL-USER> test
(1 2 3 4 5)
CL-USER> (list-insert-at test 5 'a)
(1 2 3 4 5 A)
CL-USER> (list-insert-at test 0 'a)
(A 1 2 3 4 5)
Deviled answered 17/5, 2017 at 20:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.