突然変異にはいくつかの種類があります。 1つはオブジェクトで、car
またはcdr
またはcons
を別のものに変更できます。cons
は前後に同じアドレスを持つので、それを指す変数はすべて同じものを指しますが、オブジェクトは変更されます。
(defparameter *mutating-obj* (list 1))
(defparameter *mutating-obj2* *mutating-obj*)
(setf (cdr *mutating-obj*) '(2))
ここでは、両方の変数が変更された同じ値を指しているようにオブジェクトを変更します。いずれかを評価するときは(1 2)
と表示されます。 突然変異を起こすことができるcar
とcdr
のものではないため、オブジェクトを突然変異させるので、値は最初に()
になることはありません。
可変記号にsetf
を指定すると、変数は値のアドレスの場所と考えることができます。したがって、setf
は値自体ではなく、その位置を変更します。
(defparameter *var1* '(1 2 3))
(defparameter *var2* *var1*)
ここでは、同じリストを指す2つの変数があります。私はこれを行う場合:
(push 0 *var2*)
その後*var2*
はそれが0から始まる新しいリストを指し、前の値の尾を持っているように、そのポインタが変更されました。これは、以前の値*var2*
が依然として指している*var1*
を変更しません。
値を持つ関数を呼び出すと、値は新しい変数としてバインドされ、その上でpush
を実行すると同じことが実行され、その変数が変更され、同じ値を指す他の変数は決して変更されません。
push
の一般的な使用方法は、空のリストから始めて要素を追加することです。 car
とcdr
を設定すると、空のリストを特定の値を持つ1つの要素リストに変更することはできません。
(defun make-stack()
(list 'stack-head))
(defun push-stack (element stack)
(assert (eq (car stack) 'stack-head))
(setf (cdr stack) (cons element (cdr stack)))
stack)
(defun pop-stack (stack)
(let ((popped (cadr stack)))
(setf (cdr stack) (cddr stack))
popped))
あなたが具体的に設計しない限り、しかしこれは動作しません:nil
を指しているすべての変数は、データ構造が使用されることはありませんヘッド要素を持っていた場合rplacd
((setf (cdr var) ...)
)を用いる方法が機能するように変更されていません。ですから、push
は本当に変数を変更する必要があります。 (値を変更すると思う初心者を除きます)
何も追加テストで回避することができます。 – gholk
@gholk:どうすれば変数にアクセスできないのですか? –
@gholk問題は、 'nil'はあなたが変更できない原子だということです。 'mypush'は'(mypush 1 nil) 'が' nil'を '(1)'に変えるように書くことはできません。この関数は確実にそのオブジェクトを返すことができますが、呼び出し側はその戻り値を取得し、リストを保持するすべての場所を更新する必要があります。 – Kaz