user1232236
user1232236

Reputation: 217

Common Lisp: How to return a list without the nth element of a given list?

I've a question, how to return a list without the nth element of a given list? E.g., given list: (1 2 3 2 4 6), and given n = 4, in this case the return list should be (1 2 3 4 6).

Upvotes: 7

Views: 9447

Answers (9)

BitTickler
BitTickler

Reputation: 11885

For all you haskellers out there, there is no need to twist your brains :)

(defun take (n l)
  (subseq l 0 (min n (length l))))
(defun drop (n l)
  (subseq l n))
(defun remove-nth (n l)
  (append (take (- n 1) l)
      (drop n l)))

Upvotes: 1

Jong-Hyouk Yun
Jong-Hyouk Yun

Reputation: 125

(loop :for i :in '(1 2 3 2 4 6) ; the list
              :for idx :from 0
              :unless (= 3 idx) :collect i) ; except idx=3
;; => (1 2 3 4 6)

loop macro can be very useful and effective in terms of generated code by lisp compiler and macro expander.

Test run and apply macroexpand above code snippet.

Upvotes: 2

Rorschach
Rorschach

Reputation: 32426

A destructive version, the original list will be modified (except when n < 1),

(defun remove-nth (n lst)
  (if (< n 1) (cdr lst)
    (let* ((p (nthcdr (1- n) lst))
           (right (cddr p)))
      (when (consp p)
        (setcdr p nil))
      (nconc lst right))))

That's elisp but I think those are standard lispy functions.

Upvotes: 1

Hashken
Hashken

Reputation: 4656

A much simpler solution will be as follows.

(defun remove-nth (n lst)
    (append (subseq lst 0 (- n 1)) (subseq lst n (length lst)))
)

Upvotes: 0

John Pick
John Pick

Reputation: 5650

Here's an interesting approach. It replaces the nth element of a list with a new symbol and then removes that symbol from the list. I haven't considered how (in)efficient it is though!

(defun remove-nth (n list)
    (remove (setf (nth n list) (gensym)) list))

Upvotes: 4

Samuel Edwin Ward
Samuel Edwin Ward

Reputation: 6675

A simple recursive solution:

(defun remove-nth (n list)
  (declare
    (type (integer 0) n)
    (type list list))
  (if (or (zerop n) (null list))
    (cdr list)
    (cons (car list) (remove-nth (1- n) (cdr list)))))

This will share the common tail, except in the case where the list has n or more elements, in which case it returns a new list with the same elements as the provided one.

Upvotes: 8

adaszko
adaszko

Reputation: 188

A slightly more general function:

(defun remove-by-position (pred lst)
  (labels ((walk-list (pred lst idx)
             (if (null lst)
                 lst
                 (if (funcall pred idx)
                     (walk-list pred (cdr lst) (1+ idx))
                     (cons (car lst) (walk-list pred (cdr lst) (1+ idx)))))))
    (walk-list pred lst 1)))

Which we use to implement desired remove-nth:

(defun remove-nth (n list)
  (remove-by-position (lambda (i) (= i n)) list))

And the invocation:

(remove-nth 4 '(1 2 3 2 4 6))

Edit: Applied remarks from Samuel's comment.

Upvotes: 1

danlei
danlei

Reputation: 14291

Using remove-if:

(defun foo (n list)
  (remove-if (constantly t) list :start (1- n) :count 1))

butlast/nthcdr solution (corrected):

(defun foo (n list)
  (append (butlast list (1+ (- (length list) n))) (nthcdr n list)))

Or, maybe more readable:

(defun foo (n list)
  (append (subseq list 0 (1- n)) (nthcdr n list)))

Using loop:

(defun foo (n list)
  (loop for elt in list
        for i from 1
        unless (= i n) collect elt))

Upvotes: 7

pmr
pmr

Reputation: 59811

My horrible elisp solution:

(defun without-nth (list n)
  (defun accum-if (list accum n)
    (if (not list)
        accum
          (accum-if (cdr list) (if (eq n 0) accum (cons (car list) accum)) 
            (- n 1))))
  (reverse (accum-if list '() n)))

(without-nth '(1 2 3) 1)

Should be easily portable to Common Lisp.

Upvotes: 0

Related Questions