3

Common Lisp の破壊的な DELETE 関数について少し混乱しています。アイテムがリストの最初のアイテムである場合を除いて、期待どおりに動作するようです:

CL-USER> (defvar *test* (list 1 2 3))
*TEST*
CL-USER> (delete 1 *test*)
(2 3)
CL-USER> *test*
(1 2 3)
CL-USER> (delete 2 *test*)
(1 3)
CL-USER> *test*
(1 3)
4

3 に答える 3

6

「破壊的」とは「その場で動作する」という意味ではありません。これは、操作される値の構造が、任意の、多くの場合未定義の方法で変更される可能性があることを意味します。これは、場合によっては、実装とケースが「所定の位置に」あるかのように効果を発揮する可能性があります。ただし、これは一般的に信頼できません。

破壊演算子を使用すると、操作の完了後に変数の以前の値に関心がないことをコンパイラに伝えます。その値は後で認識できないほど文字化けしていると想定し、それ以上使用しないでください。代わりに、操作の戻り値を使用する必要があります。

(let ((a (list 1 2 3)))
  (let ((b (delete 2 a)))
    (frob b))
  a)

=> You were eaten by a grue.

破壊的な操作の安全性が不明な場合は、非破壊的な操作を使用してください (removeこの場合)。

(let ((a (list 1 2 3)))
  (let ((b (remove 2 a)))
    (frob b))
  a)

=> (1 2 3)

変数の内容を本当に変更したい場合は、操作の戻り値に設定します。

(let ((a (list 1 2 3)))
  (setf a (delete 2 a))
  a)

=> (1 3)
于 2013-10-12T22:14:00.143 に答える
2

DELETECDRリストの前のコンス セルの を変更して、削除する要素の次のコンス セルを指すようにします。しかし、リストの最初の要素を削除する場合、変更する前のコンス セルはありません。

この実装は実際には言語標準によって指定されていませんが、事実上すべての実装が機能する方法です。

リストの最初の要素を削除するときに変更する前のコンス セルがないため、単純に 2 番目のコンス セルを返します。したがってDELETE、リストを変更することは許可されていますが、このケースを処理するには、結果を変数に割り当てる必要があります。また、破壊的な動作は標準でのみ許可されており、必須ではないことを強調する必要があります。そのため、一部の実装では破壊的に実装されない可能性がわずかにあり、それも考慮に入れる必要があります。

またDELETE、CDR の代わりに CAR を変更することで機能したとしても、破壊的に実行できない場合があります。たとえば、リストのすべての要素を削除します。

(setq *test* (list 'a 'a))
(delete 'a *test*)

これにより、空のリストが生成されNILます。しかし*test*、元のリストの先頭だったコンス セルがまだ含まれており、それDELETEを変更する方法はありません。したがって、次のことを行う必要があります。

(setq *test* (delete 'a *test*))

に設定*test*NILます。

于 2013-10-12T20:50:29.903 に答える