Comment supprimer / supprimer le nième élément d'une liste


9

Q:  Comment supprimer / supprimer le nième élément d'une liste.

CAVEAT : Ne supprimez pas toutes les occurrences / membres correspondant au nième élément - par exemple, eqou equal.

EXEMPLE : Supprimer le 17e élément 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)

nième ÉLÉMENT - TRICHE / LÉGENDE :

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

Réponses:


7

Eh bien, voici une version destructrice avec laquelle je serais heureux:

(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

setcarrenvoie NEWCAR, qui est un symbole non interne G<N>supprimé avec delq:

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

Vous pouvez également utiliser nilpar exemple comme NEWCAR lorsqu'il n'y a aucune valeur nulle dans la liste.


1
Astuce. L'utilisation (cons nil nil)est un peu moins chère que gensympuisque vous n'avez pas vraiment besoin d'un symbole ici.
npostavs

3

Voici une fonction simple pour supprimer le nième élément d'une 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)

Deux remarques: cela nécessite cl-lib, et ce n'est pas terriblement efficace, car il parcourt la liste plusieurs fois. Ce dernier n'est probablement perceptible que pour les longues listes.

Voici des versions destructives et non destructives qui ne nécessitent pas cl-lib(encore une fois, pas terriblement efficaces):

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

Merci pour la réponse alternative qui nous aide à apprendre à cl-subseqpartir de la cl-libbibliothèque.
Lawlist

Tu m'as battu avec le nthcdr-way ;-). Je ne l'ai vu qu'au deuxième coup d'œil. Supprimé ma réponse ...
Tobias

3

Voici une autre version non destructive qui utilise 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

Voici une réponse utilisant simplement la récursivité. Nous vérifions d'abord si la liste est vide, auquel cas nous renvoyons la liste vide. Ensuite, nous vérifions si nous supprimons le 0 ème élément de la liste, auquel cas tout ce que nous voulons est le cdrde la liste. Si nous n'avons pas atteint l'un de ces cas de base, nous répétons en supprimant le n-1 ème élément cdrde la liste, puis consle carde la liste d'origine sur le résultat de l'appel récursif.

Devrait être très efficace. Fonctionne en temps linéaire.

(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
Cela fait n appels récursifs (utilise donc un espace de pile linéaire), donc je doute que "très efficace" soit une bonne description. Et il court le risque de débordement de pile pour les gros n.
npostavs

2

Surpris de voir cl-delete/remove-ifn'a pas été mentionné:

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

C'est un temps linéaire et devrait être raisonnablement efficace, bien que je m'attende à un peu plus lentement que la réponse de wvxvw car elle passe par des chemins de code plus génériques.


0

Je n'étais pas satisfait de la réponse acceptée car elle ne semble pas être destructrice pour nth = 0. J'ai trouvé ce qui suit:

Version non destructive:

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

seq.elles fonctions sont nouvelles dans emacs 25.1. Dans les anciennes versions, vous devrez peut-être

(require 'seq)

Version destructrice, même pour nième = 0:

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

Lorsque vous manipulez des chaînes Lisp, "destructif" signifie généralement "est autorisé à gâcher la liste d'entrée comme bon lui semble". Cela ne force pas la modification de la liste d'entrée. Vous semblez vouloir une opération qui est effectuée "sur place" (c'est-à-dire qu'elle ne renvoie pas la réponse mais modifie simplement son argument), mais delete-nth-element ne peut pas fonctionner "sur place" dans le cas où numest 0 et lstest une liste d'un seul élément.
Stefan

@Stefan: Merci pour votre observation. En effet, delete-nth-elementnon seulement supprimer un seul élément, mais plutôt le remplacer par nil, se terminant par au (nil)lieu de prévu (). +1.
inamist
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.