Common Lisp: как вернуть список без n-го элемента данного списка?

у меня вопрос, как вернуть список без n-го элемента данного списка? Е. Г., приведенный список: (1 2 3 2 4 6), а с учетом n = 4 в этом случае возврат денежных средств должен быть (1 2 3 4 6).

7 ответов


простое рекурсивное решение:

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

это будет общий хвост, за исключением случая, когда список имеет n или более элементов, в этом случае он возвращает новый список с теми же самыми элементами, так как одна.


используя remove-if:

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

butlast/nthcdr решение (исправлено):

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

или, может быть, более читаемым:

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

используя loop:

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

немного более общая функция:

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

который мы используем для реализации желаемого remove-nth:

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

и ссылки:

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

Edit: примененные замечания из комментария Сэмюэля.


вот интересный подход. Он заменяет N-й элемент списка с новым символом, а затем удаляет этот символ из списка. Я не рассматривал, как (в)эффективно это, хотя!

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

мое ужасное решение elisp:

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

должен быть легко переносимым на общий Lisp.


гораздо более простым решением будет следующее.

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

разрушительная версия, исходный список будет изменен (за исключением случаев, когда n

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

это elisp, но я думаю, что это стандартные функции lispy.