Come rimuovere / cancellare l'ennesimo elemento di un elenco


9

D:  Come rimuovere / eliminare l'ennesimo elemento di un elenco.

CAVEAT : Do non rimuovere tutte le occorrenze / i membri corrispondenti n-esimo elemento - ad esempio, eqo equal.

ESEMPIO : rimuovere il 17 ° elemento di:

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

ennesimo ELEMENTO - SCHEDA / LEGGENDA DI CHEAT :

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

Risposte:


7

Bene, ecco una versione distruttiva di cui sarei felice:

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

4

setcarrestituisce NEWCAR, che è un simbolo non modificato G<N>cancellato con delq:

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

È inoltre possibile utilizzare nilad esempio NEWCAR quando non ci sono valori nulli nell'elenco.


1
Trucco pulito. L'uso (cons nil nil)è un po 'più economico rispetto al gensymfatto che in realtà non è necessario un simbolo qui.
npostavs

3

Ecco una semplice funzione per rimuovere l'ennesimo elemento da un elenco:

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

Due note: richiede cl-libe non è terribilmente efficiente, poiché scorre l'elenco alcune volte. Quest'ultimo è probabilmente evidente solo per lunghi elenchi.

Ecco le versioni distruttive e non distruttive che non richiedono cl-lib(di nuovo, non terribilmente efficiente):

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

Grazie per la risposta alternativa che ci aiuta a insegnare come cl-subseqdalla cl-libbiblioteca.
elenco delle leggi

Mi hai battuto con la nthcdrstrada ;-). L'ho visto solo al secondo sguardo. Cancellata la mia risposta ...
Tobias,

3

Ecco un'altra versione non distruttiva che utilizza 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)

3

Ecco una risposta usando solo la ricorsione. Per prima cosa controlliamo se l'elenco è vuoto, nel qual caso restituiamo l'elenco vuoto. Quindi controlliamo per vedere se stiamo rimuovendo il 0 ° elemento dell'elenco, nel qual caso tutto ciò che vogliamo è cdrl'elenco. Se non abbiamo individuato uno di questi casi di base, ricorriamo rimuovendo il n-1 ° elemento cdrdell'elenco e quindi consl' carelenco dell'elenco originale sul risultato dalla chiamata ricorsiva.

Dovrebbe essere molto efficiente. Funziona in tempo lineare.

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

1
Questo rende n chiamate ricorsive (quindi utilizza uno spazio di stack lineare), quindi dubito che "molto efficiente" sia una buona descrizione. E corre il pericolo di overflow dello stack per big n.
npostavs

2

Sorpreso di vedere cl-delete/remove-ifnon è stato menzionato:

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

Questo è un tempo lineare e dovrebbe essere ragionevolmente efficiente, anche se mi aspetto leggermente più lento rispetto alla risposta di wvxvw mentre passa attraverso alcuni codepati più generici.


0

Non ero contento della risposta accettata perché non sembra essere distruttivo per nth = 0. Ho pensato a quanto segue:

Versione non distruttiva:

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

seq.elle funzioni sono nuove in emacs 25.1. Nelle versioni precedenti potrebbe essere necessario

(require 'seq)

Versione distruttiva, anche per nth = 0:

(defun delete-nth-element (num lst)
  (if (zerop num)
      (setf (car lst) (cadr lst)
            (cdr lst) (cddr lst))
    (pop (nthcdr num lst))))

Quando si manipolano le stringhe di Lisp "distruttive" di solito significa "è consentito rovinare l'elenco di input come meglio ritiene". Non forzare la lista di input da modificare. Sembra che tu voglia un'operazione eseguita "sul posto" (cioè non restituisce la risposta ma invece modifica solo il suo argomento), ma delete-nth-element non può funzionare "sul posto" nel caso in cui numè 0 ed lstè un elenco di un singolo elemento.
Stefan,

@Stefan: grazie per la tua osservazione. Effettivamente delete-nth-elementnon si limita a eliminare un singolo elemento, ma piuttosto lo sostituisce con nil, finendo con (nil)invece del previsto (). +1.
Inamista
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.