Cách xóa / xóa phần tử thứ n của danh sách


9

Q:  Cách xóa / xóa phần tử thứ n của danh sách.

Nên biết trước : Đỗ không loại bỏ tất cả các lần xuất hiện / thành viên phù hợp với yếu tố thứ n - ví dụ, eqhoặc equal.

VÍ DỤ : Xóa phần tử thứ 17 của:

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

PHẦN NẾU - BẢNG GIÁ / LEGEND :

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

Câu trả lời:


7

Chà, đây là một phiên bản phá hoại mà tôi hài lòng:

(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

setcartrả về NEWCAR, một biểu tượng không liên kết G<N>đã bị xóa với delq:

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

Bạn cũng có thể sử dụng nilví dụ như NEWCAR khi không có giá trị nào trong danh sách.


1
Trò hay. Sử dụng (cons nil nil)rẻ hơn một chút so với gensymkhi bạn không thực sự cần một biểu tượng ở đây.
npostavs 17/03/19

3

Đây là một chức năng đơn giản để loại bỏ phần tử thứ n khỏi danh sách:

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

Hai lưu ý: nó đòi hỏi cl-lib, và nó không hiệu quả khủng khiếp, vì nó đi qua danh sách một vài lần. Thứ hai có lẽ chỉ đáng chú ý cho danh sách dài.

Dưới đây là các phiên bản phá hoại và không phá hủy không yêu cầu cl-lib(một lần nữa, không hiệu quả khủng khiếp):

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

Cảm ơn bạn đã trả lời thay thế giúp dạy chúng tôi làm thế nào cl-subseqtừ cl-libthư viện.
luật

Bạn đánh tôi với nthcdr-way ;-). Chỉ nhìn thấy nó trên cái nhìn thứ hai. Đã xóa câu trả lời của tôi ...
Tobias

3

Đây là một phiên bản không phá hủy khác sử dụng 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

Đây là một câu trả lời chỉ sử dụng đệ quy. Đầu tiên chúng tôi kiểm tra xem danh sách có trống không, trong trường hợp đó chúng tôi trả về danh sách trống. Sau đó, chúng tôi kiểm tra xem liệu chúng tôi có xóa phần tử thứ 0 của danh sách hay không, trong trường hợp đó tất cả những gì chúng tôi muốn là cdrdanh sách. Nếu chúng tôi không gặp phải một trong những trường hợp cơ bản đó, chúng tôi sẽ tái diễn bằng cách xóa phần tử thứ 1 cdrcủa danh sách và sau đó conscardanh sách gốc vào kết quả từ cuộc gọi đệ quy.

Nên rất hiệu quả. Chạy trong thời gian tuyến tính.

(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
Điều này làm cho n cuộc gọi đệ quy (do đó sử dụng không gian ngăn xếp tuyến tính), vì vậy tôi nghi ngờ "rất hiệu quả" là một mô tả hay. Và nó có nguy cơ tràn stack cho n lớn.
npostavs 17/03/19

2

Ngạc nhiên khi thấy cl-delete/remove-ifkhông được đề cập:

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

Đây là thời gian tuyến tính và nên có hiệu quả hợp lý, mặc dù tôi mong đợi chậm hơn so với câu trả lời của wvxvw khi nó đi qua một số loại tiền mã hóa chung hơn.


0

Tôi không hài lòng với câu trả lời được chấp nhận vì nó dường như không bị phá hủy trong nth = 0. Tôi đã đưa ra những điều sau:

Phiên bản không phá hủy:

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

seq.elcác chức năng là mới trong emacs 25.1. Trong các phiên bản cũ hơn, bạn có thể cần phải

(require 'seq)

Phiên bản hủy diệt, ngay cả đối với nth = 0:

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

Khi thao tác các chuỗi Lisp, "phá hủy" thường có nghĩa là "được phép làm rối danh sách đầu vào khi nó thấy phù hợp". Nó không buộc danh sách đầu vào phải được sửa đổi. Bạn dường như muốn một hoạt động được thực hiện "tại chỗ" (nghĩa là nó không trả về câu trả lời mà thay vào đó chỉ sửa đổi đối số của nó), nhưng delete-nth-element không thể hoạt động "tại chỗ" trong trường hợp numlà 0 và lstlà danh sách của một thành phần.
Stefan

@Stefan: Cảm ơn bạn đã quan sát của bạn. Thật vậy delete-nth-element, không chỉ xóa một yếu tố duy nhất mà thay thế nó bằng nil, kết thúc bằng (nil)thay vì mong đợi (). +1.
inamist
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.