Como remover / excluir o enésimo elemento de uma lista

9

P:  Como remover / excluir o enésimo elemento de uma lista.

CAVEAT : Do não remover todas as ocorrências / membros correspondentes enésimo elemento - por exemplo, eqou equal.

EXEMPLO : Remova o 17º elemento 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 - FOLHA DE TRABALHO / LEGENDA :

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 leis
fonte

Respostas:

7

Bem, aqui está uma versão destrutiva que eu ficaria feliz com:

(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
fonte
4

setcarretorna NEWCAR, que é um símbolo não interno G<N>excluído com delq:

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

Você também pode usar, nilpor exemplo, como NEWCAR quando não houver valores nulos na lista.

mutbuerger
fonte
11
Truque legal. Usar (cons nil nil)é um pouco mais barato do que gensymjá que você não precisa de um símbolo aqui.
N
3

Aqui está uma função simples para remover o enésimo elemento de uma 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)

Duas notas: requer cl-libe não é muito eficiente, pois percorre a lista algumas vezes. O último provavelmente é perceptível apenas para listas longas.

Aqui estão as versões destrutivas e não destrutivas que não exigem cl-lib(novamente, não são terrivelmente 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
fonte
Obrigado pela resposta alternativa que nos ajuda a aprender a cl-subseqpartir da cl-libbiblioteca.
lawlist
Você me venceu com o nthcdrcaminho ;-). Só o vi pela segunda vez. Excluiu minha resposta ...
Tobias
3

Aqui está outra versão não destrutiva que usa 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
fonte
3

Aqui está uma resposta usando apenas recursão. Primeiro, verificamos se a lista está vazia; nesse caso, retornamos a lista vazia. Em seguida, verificamos se estamos removendo o elemento da lista; nesse caso, tudo o que queremos é o cdrda lista. Se não atingimos um desses casos base, recorremos removendo o n- ésimo elemento da cdrlista e, em seguida, conso carda lista original no resultado da chamada recursiva.

Deve ser muito eficiente. É executado em tempo linear.

(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
fonte
11
Isso faz n chamadas recursivas (portanto, usa espaço de pilha linear), então duvido que "muito eficiente" seja uma boa descrição. E corre o risco de excesso de pilha para grandes n.
npostavs 17/03/19
2

Surpreso ao ver cl-delete/remove-ifnão foi 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 é um tempo linear e deve ser razoavelmente eficiente, embora eu espere um pouco mais lento do que a resposta do wvxvw, à medida que ele passa por alguns caminhos de código mais genéricos.

npostavs
fonte
0

Eu não estava feliz com a resposta aceita, porque não parece ser destrutivo para nésimo = 0. Eu vim com o seguinte:

Versão não destrutiva:

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

seq.elfunções são novas no emacs 25.1. Nas versões mais antigas, pode ser necessário

(require 'seq)

Versão destrutiva, mesmo para enésimo = 0:

(defun delete-nth-element (num lst)
  (if (zerop num)
      (setf (car lst) (cadr lst)
            (cdr lst) (cddr lst))
    (pop (nthcdr num lst))))
inamista
fonte
Ao manipular seqüências de caracteres Lisp "destrutivas" geralmente significa "é permitido que atrapalhe a lista de entradas como entender". Não força a lista de entrada a ser modificada. Você parece querer uma operação executada "no local" (ou seja, não retorna a resposta, mas apenas modifica seu argumento), mas delete-nth-element não pode funcionar "no local" no caso em que numé 0 e lsté uma lista de uma única elemento.
Stefan
@ Stefan: Obrigado por sua observação. Na verdade delete-nth-element, não apenas exclui um único elemento, mas o substitui por nil, terminando com em (nil)vez do esperado (). +1.
inamist 13/04