リストのn番目の要素を削除/削除する方法


9

Q:  リストのn番目の要素を削除/削除する方法。

警告:n番目の要素に一致するすべての出現/メンバーを削除しないでください(例:eqまたは)equal

:次の17番目の要素を削除します。

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

回答:


7

まあ、これは私が満足する破壊的なバージョンです:

(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

setcarNEWCARを返します。これは、次のコマンドでG<N>削除された非インターンシンボルdelqです。

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

nilリストにnil値がない場合、たとえばNEWCARとして使用することもできます。


1
きちんとしたトリック。ここで実際にシンボルを必要としないので(cons nil nil)、使用することは少し安いgensymです。
npostavs

3

リストからn番目の要素を削除する簡単な関数を次に示します。

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

2つの注意事項:が必要ですがcl-lib、リストを数回処理するため、効率はそれほど良くありません。後者はおそらく長いリストでのみ顕著です。

以下はcl-lib、必要としない破壊的なバージョンと非破壊的なバージョンです(ここでも、非常に効率的ではありません)。

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

図書館cl-subseqからの行き方を教えてくれる別の答えをありがとうcl-lib
弁護士リスト2017年

nthcdr-way ;-)で私を倒しました。一見しただけでした。私の答えを削除しました...
トビアス

3

これが使用する別の非破壊バージョン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

これは、再帰のみを使用した答えです。最初に、リストが空かどうかを確認します。この場合、空のリストを返します。次に、リストの0 番目の要素を削除するかどうかを確認します。この場合、必要なのはリストの要素だけですcdr。我々は、これらの基本例1をヒットしていない場合、我々は、n-1除去することにより、再発番目の要素cdrリストのをして、再帰呼び出しの結果に元のリストの。conscar

非常に効率的でなければなりません。線形時間で実行されます。

(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
これはn回の再帰呼び出し(したがって線形スタック空間を使用する)を行うので、「非常に効率的」であるとは言いがたいです。そして、それはbig nのスタックオーバーフローの危険を冒します。
npostavs

2

見て驚いたことcl-delete/remove-ifは言及されていません:

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

これは線形時間であり、かなり効率的ですが、いくつかのより一般的なコードパスを通過するため、wvxvwの回答よりもわずかに遅いと予想されます。


0

受け入れられた答えはnth = 0では破壊的ではないようで、満足していませんでした。次のことを思いつきました。

非破壊バージョン:

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

seq.el関数はemacs 25.1で新しく追加されました。古いバージョンでは、

(require 'seq)

nth = 0の場合でも破壊的なバージョン:

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

Lisp文字列を操作する場合、「破壊的」とは通常、「適切と思われる入力リストを台無しにできる」ことを意味します。入力リストの変更は強制されません。「インプレース」で実行される操作(つまり、答えを返さず、代わりに引数を変更するだけ)が必要なようですが、が0 で、単一のリストである場合、「インプレース」で機能しdelete-nth-element ません。素子。numlst
ステファン

@Stefan:どうもありがとうございました。確かにdelete-nth-element、単一の要素を削除するだけでなく、それをで置き換えnil、最終的(nil)に期待されるの代わりにで終わり()ます。+1。
イナミスト
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.