3
(setf list (loop for i from 1 to 12 collect i))
(defun removef (item seq)
  (setf seq (remove item seq)))


CL-USER> (removef 2 list)
(1 3 4 5 6 7 8 9 10 11 12)

CL-USER> (removef 3 list)
(1 2 4 5 6 7 8 9 10 11 12)

removef変数を実際に変更しないのはなぜですか?

4

2 に答える 2

7

Common Lisp では、パラメーターは「ID によって」渡されます (この用語は、Allegro Common Lisp 実装の開発者の 1 人であるD. Rettig に遡ります)。(ヒープ オブジェクトへの) ポインターが値によって渡されると考えてください。これは、ほとんどの Lisp オブジェクト (文字列、ベクトル、そしてもちろんリストなど) に当てはまります。実装には即値もある可能性があるため、状況は少し複雑になりますが、それは別としてここがポイント)。

setfofは、関数のseq(プライベート、レキシカル) 変数バインディングを変更します。この変更は 以外では表示されませんremovef

呼び出し時に周囲の環境に影響を与えることができるようremovefにするには、マクロにする必要があります。

(defmacro removef (element place)
   `(setf ,place (remove ,element ,place)))

一般化された参照setfの概念を見てみたいと思うかもしれません。上記の Iのマクロ バージョンは、実際に行うべき方法ではないことに注意してください。詳細については、約とその醜い詳細をお読みください。removefget-setf-expansion

リストを破壊的に変更するだけの場合はdelete、remove の代わりに使用することを検討してください。ただし、意図しない結果が生じる可能性があることに注意してください。

(delete 2 '(1 2 3 4))

ANSI 標準では許可されていません (リテラル オブジェクト、つまりコードの一部を破壊的に変更しています)。この例では、間違いを簡単に見つけることができますが、一部の呼び出しスタックで 7 フレームの深さで、元が完全には明らかでない値を処理している場合、これは実際の問題になります。そしてとにかく、さえ

(setf list (list 1 2 3 4))
(delete 1 list)
list

最初は驚くかもしれませんが、

(setf list (list 1 2 3 4))
(delete 2 list)
list

「働く」ようです。基本的に、最初の例は意図したとおりには機能しません。関数deleteには元のバージョンの と同じ問題があるためですremovef。つまり、呼び出し元の変数の概念を変更できないlistため、破壊的なバージョンの場合でも、正しい方法は次のとおりです。 :

(setf list (delete 1 (list 1 2 3 4)))
于 2013-06-13T15:07:20.277 に答える