Cómo eliminar / eliminar el enésimo elemento de una lista

9

P:  Cómo eliminar / eliminar el enésimo elemento de una lista.

PRECAUCIÓN : no elimine todas las ocurrencias / miembros que coincidan con el enésimo elemento, por ejemplo, eqo equal.

EJEMPLO : Eliminar el elemento 17 de:

'(a b c d e f g h i j k l m n o p q r s t u v w x y z)

Enésimo elemento - Hoja de trucos / leyenda :

element 0:   a
element 1:   b
element 2:   c
element 3:   d
element 4:   e
element 5:   f
element 6:   g
element 7:   h
element 8:   i
element 9:   j
element 10:  k
element 11:  l
element 12:  m
element 13:  n
element 14:  o
element 15:  p
element 16:  q
element 17:  r
element 18:  s
element 19:  t
element 20:  u
element 21:  v
element 22:  w
element 23:  x
element 24:  y
element 25:  z
lista de leyes
fuente

Respuestas:

7

Bueno, aquí hay una versión destructiva con la que estaría feliz:

(defun remove-nth-element (nth list)
  (if (zerop nth) (cdr list)
    (let ((last (nthcdr (1- nth) list)))
      (setcdr last (cddr last))
      list)))

(remove-nth-element 0 (list 1 2 3 4 5))
(2 3 4 5)

(remove-nth-element 1 (list 1 2 3 4 5))
(1 3 4 5)

(remove-nth-element 2 (list 1 2 3 4 5))
(1 2 4 5)

(remove-nth-element 3 (list 1 2 3 4 5))
(1 2 3 5)

(remove-nth-element 4 (list 1 2 3 4 5))
(1 2 3 4)

(remove-nth-element 5 (list 1 2 3 4 5))
(1 2 3 4 5)
wvxvw
fuente
4

setcardevuelve NEWCAR, que es un símbolo no interno G<N>eliminado con delq:

(let ((l '(1 2 3 4 5 6 7)))
  (delq (setcar (nthcdr 5 l) (gensym)) l)) ;⇒ (1 2 3 4 5 7)

También puede usar, nilpor ejemplo, como NEWCAR cuando no hay valores nulos en la lista.

mutbuerger
fuente
1
Buen truco. Usar (cons nil nil)es un poco más barato que gensymya que en realidad no necesitas un símbolo aquí.
npostavs
3

Aquí hay una función simple para eliminar el enésimo elemento de una lista:

(defun remove-nth (n list)
  "Remove the nth element of a list."
  (if (> n (length list))
      list
    (append (cl-subseq list 0 n)
            (cl-subseq list (1+ n)))))

(setq test-list '(a b c d e))
(remove-nth 3 test-list)                ; => (a b c e)

Dos notas: requiere cl-lib, y no es terriblemente eficiente, ya que recorre la lista varias veces. Este último probablemente solo sea notable para largas listas.

Aquí hay versiones destructivas y no destructivas que no requieren cl-lib(nuevamente, no son terriblemente eficientes):

(defun delete-nth (n list)
  "Delete the nth element of a list (destructive)."
  (if (>= n (length list))
      list
    (setf (nthcdr n list) (nthcdr (1+ n) list))))

(defun remove-nth (n list)
  "Remove the nth element of a list."
  (if (>= n (length list))
      list
    (let ((list (copy-tree list)))
      (setf (nthcdr n list) (nthcdr (1+ n) list))
      list)))
Dan
fuente
Gracias por la respuesta alternativa que nos ayuda a enseñarnos cómo hacerlo cl-subseqdesde la cl-libbiblioteca.
ley el
Me nthcdrganaste con el camino ;-). Solo lo vi en la segunda mirada. Borré mi respuesta ...
Tobias
3

Aquí hay otra versión no destructiva que utiliza cl-loop:

(defun remove-nth-element (nth list)
  "Return a copy of a LIST, with NTH element removed."
  (loop for i in list
        for idx from 0
        unless (= idx nth)
        collect i))
      remove-nth-element

(remove-nth-element 0 (list 1 2 3 4 5))
      (2 3 4 5)

(remove-nth-element 1 (list 1 2 3 4 5))
      (1 3 4 5)

(remove-nth-element 2 (list 1 2 3 4 5))
      (1 2 4 5)

(remove-nth-element 3 (list 1 2 3 4 5))
      (1 2 3 5)

(remove-nth-element 4 (list 1 2 3 4 5))
      (1 2 3 4)

(remove-nth-element 5 (list 1 2 3 4 5))
      (1 2 3 4 5)
xuchunyang
fuente
3

Aquí hay una respuesta usando solo recursividad. Primero verificamos si la lista está vacía, en cuyo caso devolvemos la lista vacía. A continuación, comprobamos para ver si vamos a eliminar el 0 º elemento de la lista, en cuyo caso todo lo que queremos es el cdrde la lista. Si no hemos golpeado uno de esos casos base, recurrimos al eliminar el n-1 º elemento de la cdrde la lista, y luego consla carde la lista original en el resultado de la llamada recursiva.

Debería ser muy eficiente. Se ejecuta en tiempo lineal.

(defun remove-nth-element (nth list)
  (cond
   ((equal nil list) list)
   ((zerop nth) (cdr list))
   (t (cons (car list)
            (remove-nth-element (- nth 1)
                                (cdr list))))))

;; Examples:
(remove-nth-element 5 '(0 1 2 3 4 5 6 7))
  ;; '(0 1 2 3 4 6 7)
(remove-nth-element 9 '(0 1 2 3 4 5 6 7))
  ;; '(0 1 2 3 4 5 6 7)
(remove-nth-element 0 '(0 1 2 3 4 5 6 7))
  ;; '(1 2 3 4 5 6 7)
Kevin Johnson
fuente
1
Esto hace n llamadas recursivas (por lo tanto, utiliza espacio lineal de pila), por lo que dudo que "muy eficiente" sea una buena descripción. Y corre el peligro de desbordamiento de pila para n grande.
npostavs
2

Sorprendido de ver cl-delete/remove-ifno fue mencionado:

(require 'cl-lib)
(defun delete-nth (nth list) ; Destructive version.
  (cl-delete-if (lambda (_) t) list :start nth :end (1+ nth)))
(defun remove-nth (nth list)
  (cl-remove-if (lambda (_) t) list :start nth :end (1+ nth)))

Este es un tiempo lineal, y debería ser razonablemente eficiente, aunque espero que sea un poco más lento que la respuesta de wvxvw, ya que pasa por algunas rutas de código más genéricas.

npostavs
fuente
0

No estaba contento con la respuesta aceptada porque no parece ser destructiva para nth = 0. Se me ocurrió lo siguiente:

Versión no destructiva:

(defun remove-nth-element (num lst)
  (append (seq-take lst num) (seq-drop lst (1+ num))))

seq.elLas funciones son nuevas en emacs 25.1. En versiones anteriores puede que necesite

(require 'seq)

Versión destructiva, incluso para nth = 0:

(defun delete-nth-element (num lst)
  (if (zerop num)
      (setf (car lst) (cadr lst)
            (cdr lst) (cddr lst))
    (pop (nthcdr num lst))))
inamista
fuente
Al manipular las cadenas de Lisp, "destructivo" generalmente significa "se le permite desordenar la lista de entrada como mejor le parezca". No obliga a modificar la lista de entrada. Parece que quiere una operación que se realiza "en el lugar" (es decir, no devuelve la respuesta, sino que simplemente modifica su argumento), pero delete-nth-element no puede funcionar "en el lugar" en el caso donde numes 0 y lstes una lista de un solo elemento.
Stefan
@Stefan: Gracias por tu observación. De hecho delete-nth-element, no solo elimina un solo elemento, sino que lo reemplaza con nil, terminando en (nil)lugar de lo esperado (). +1.
inamista