Cara menghapus / menghapus elemen ke-n dari daftar

9

T:  Cara menghapus / menghapus elemen ke-n dari daftar.

Peringatan : Do tidak menghapus semua kejadian / anggota pencocokan elemen ke-n - misalnya, eqatau equal.

CONTOH : Hapus elemen ke-17 dari:

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

UNSUR n - LEMBAR / LEGENDA 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
daftar hukum
sumber

Jawaban:

7

Nah, ini versi destruktif yang akan saya senangi:

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

setcarmengembalikan NEWCAR, yang merupakan simbol tidak G<N>dihapus yang dihapus dengan delq:

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

Anda juga dapat menggunakan nilmisalnya sebagai NEWCAR ketika tidak ada nilai nol dalam daftar.

mutbuerger
sumber
1
Trik rapi. Menggunakannya (cons nil nil)sedikit lebih murah daripada gensymkarena Anda sebenarnya tidak memerlukan simbol di sini.
npostavs
3

Berikut adalah fungsi sederhana untuk menghapus elemen ke-n dari daftar:

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

Dua catatan: itu membutuhkan cl-lib, dan itu tidak terlalu efisien, karena berjalan melalui daftar beberapa kali. Yang terakhir mungkin hanya terlihat untuk daftar panjang.

Berikut adalah versi destruktif dan non-destruktif yang tidak memerlukan cl-lib(sekali lagi, tidak terlalu efisien):

(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
sumber
Terima kasih atas jawaban alternatif yang membantu mengajari kami cara cl-subseqdari cl-libperpustakaan.
Gugatan
Anda mengalahkan saya dengan nthcdrcara ;-). Apakah hanya melihatnya pada pandangan kedua. Menghapus jawaban saya ...
Tobias
3

Berikut ini adalah versi non-destruktif lain yang menggunakan 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
sumber
3

Inilah jawaban menggunakan rekursi saja. Pertama kita periksa untuk melihat apakah daftar itu kosong, dalam hal ini kita mengembalikan daftar kosong. Kemudian kita periksa untuk melihat apakah kita menghapus elemen ke- 0 dari daftar, dalam hal ini yang kita inginkan adalah cdrdaftar. Jika kita tidak memukul salah satu kasus dasar, kita kambuh dengan menghapus n-1 th elemen dari cdrdaftar, dan kemudian consyang cardari daftar asli ke hasil dari panggilan rekursif.

Harus sangat efisien. Berjalan dalam waktu linier.

(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
sumber
1
Ini membuat n panggilan rekursif (karenanya menggunakan ruang stack linear), jadi saya ragu "sangat efisien" adalah deskripsi yang baik. Dan itu menjalankan bahaya stack overflow untuk n besar.
npostavs
2

Terkejut melihat cl-delete/remove-iftidak disebutkan:

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

Ini adalah waktu linier, dan harusnya cukup efisien, meskipun saya perkirakan sedikit lebih lambat daripada jawaban wvxvw saat melewati beberapa codepath yang lebih umum.

npostavs
sumber
0

Saya tidak senang dengan jawaban yang diterima karena sepertinya tidak merusak untuk n = = 0. Saya datang dengan yang berikut:

Versi tidak merusak:

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

seq.elfungsi baru di emacs 25.1. Dalam versi yang lebih lama Anda mungkin perlu

(require 'seq)

Versi destruktif, bahkan untuk n = 0:

(defun delete-nth-element (num lst)
  (if (zerop num)
      (setf (car lst) (cadr lst)
            (cdr lst) (cddr lst))
    (pop (nthcdr num lst))))
inamist
sumber
Ketika memanipulasi string Lisp, "destruktif" biasanya berarti "diizinkan untuk mengacaukan daftar input sesuai keinginan". Itu tidak memaksa daftar input untuk dimodifikasi. Anda sepertinya menginginkan operasi yang dilakukan "di tempat" (artinya tidak mengembalikan jawaban tetapi hanya mengubah argumennya), tetapi delete-nth-element tidak dapat bekerja "di tempat" dalam kasus di mana num0 dan lstmerupakan daftar satu elemen.
Stefan
@Stefan: Terima kasih atas pengamatan Anda. Memang delete-nth-elementtidak hanya menghapus satu elemen tetapi menggantikannya dengan nil, berakhir dengan (nil)bukannya yang diharapkan (). +1.
inamist