So entfernen / löschen Sie das n-te Element einer Liste


9

F:  So entfernen / löschen Sie das n-te Element einer Liste.

CAVEAT : Entfernen Sie nicht alle Vorkommen / Elemente, die mit dem n-ten Element übereinstimmen - z . B. eqoder equal.

BEISPIEL : Entfernen Sie das 17. Element von:

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

n. ELEMENT - CHEAT SHEET / LEGENDE :

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

Antworten:


7

Nun, hier ist eine destruktive Version, mit der ich zufrieden sein würde:

(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

setcargibt NEWCAR zurück, ein nicht interniertes Symbol, das G<N>gelöscht wurde mit delq:

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

Sie können nilbeispielsweise auch als NEWCAR verwenden, wenn die Liste keine Nullwerte enthält.


1
Ordentlicher Trick. Die Verwendung (cons nil nil)ist etwas billiger als gensymda Sie hier eigentlich kein Symbol benötigen.
npostavs

3

Hier ist eine einfache Funktion zum Entfernen des n-ten Elements aus einer Liste:

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

Zwei Anmerkungen: Es erfordert cl-lib, und es ist nicht besonders effizient, da es einige Male durch die Liste geht. Letzteres fällt wahrscheinlich nur bei langen Listen auf.

Hier sind destruktive und zerstörungsfreie Versionen, die nicht erforderlich sind cl-lib(wiederum nicht besonders effizient):

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

Vielen Dank für die alternative Antwort, die uns hilft, wie man cl-subseqaus der cl-libBibliothek kommt.
Lawlist

Du hast mich mit dem nthcdrWeg geschlagen ;-). Habe es erst auf den zweiten Blick gesehen. Löschte meine Antwort ...
Tobias

3

Hier ist eine andere zerstörungsfreie Version, die verwendet 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

Hier ist eine Antwort mit nur Rekursion. Zuerst prüfen wir, ob die Liste leer ist. In diesem Fall geben wir die leere Liste zurück. Dann überprüfen wir, ob wir das 0 entfernen te Element der Liste, wobei in diesem Fall alles , was wir das wollen , cdrin der Liste. Wenn wir keinen dieser Basisfälle getroffen haben, wiederholen wir das n-1- te Element cdrder Liste und dann consdas carder ursprünglichen Liste auf das Ergebnis des rekursiven Aufrufs.

Sollte sehr effizient sein. Läuft in linearer Zeit.

(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
Dies führt zu n rekursiven Aufrufen (verwendet daher linearen Stapelraum), daher bezweifle ich, dass "sehr effizient" eine gute Beschreibung ist. Und es besteht die Gefahr eines Stapelüberlaufs für große n.
npostavs

2

Überrascht zu sehen cl-delete/remove-ifwurde nicht erwähnt:

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

Dies ist eine lineare Zeit und sollte einigermaßen effizient sein, obwohl ich davon ausgehe , dass sie geringfügig langsamer ist als die Antwort von wvxvw, da sie einige allgemeinere Codepfade durchläuft.


0

Ich war mit der akzeptierten Antwort nicht zufrieden, da sie für nth = 0 nicht destruktiv zu sein scheint. Ich habe Folgendes gefunden:

Zerstörungsfreie Version:

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

seq.elFunktionen sind neu in Emacs 25.1. In älteren Versionen müssen Sie möglicherweise

(require 'seq)

Destruktive Version, auch für nth = 0:

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

Bei der Manipulation von Lisp-Strings bedeutet "destruktiv" normalerweise "darf die Eingabeliste nach Belieben durcheinander bringen". Dabei spielt es keine Gewalt die Eingangsliste geändert werden. Sie scheinen eine Operation zu wollen, die "an Ort und Stelle" ausgeführt wird (dh sie gibt die Antwort nicht zurück, sondern ändert nur ihr Argument), delete-nth-element kann jedoch nicht "an Ort und Stelle" arbeiten, wenn num0 ist und lsteine Liste einer einzelnen ist Element.
Stefan

@Stefan: Danke für deine Beobachtung. In delete-nth-elementder Tat wird nicht nur ein einzelnes Element gelöscht, sondern durch ersetzt nil, was (nil)nicht erwartet wird (). +1.
Inamist
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.