2017-01-24 10 views
0

は、私は二つのリスト(setq x '(1 2 3))(setq y (list 1 (cadr x) 3)を持っていると思ったが、私は(setf (cadr x) 'a)(cadr y)は私にAない2を与えるだろうと言っているよう(cadr y)が実際に(cadr x) を指すように望んでいたと言います。それを行う方法はありますか?部分的に共有データ構造のCommon Lisp

答えて

4

x(list 1 2 3)である代わりに、代わりにリテラルが標準で定義されていない結果をもたらすからです。

あなたの例では、共有リスト構造はまったくありません。あなたが持っているものは、同じ値を指す異なるリスト構造です。 (setf (cadr x) 'a)は、xというリスト構造を変更し、異なる値を指しています。値2を別のものに変更しません。結果は1つの場所だけを変更したということです。

(let ((tmp (list 2 3))) 
    (defparameter *x* (cons 1 tmp)) 
    (defparameter *y* (cons 'a tmp))) 

*x* ; ==> (1 2 3) 
*y* ; ==> (a 2 3) 

;; the cdr of both are the same 
(eq (cdr *x*) (cdr *y*)) ; ==> t 

;; mutating the cadr changes both since both list use the same cons 
(setf (cadr *x*) 'q) 
*x* ; ==> (1 q 3) 
*y* ; ==> (a q 3) 

共有可変オブジェクト

:共有リスト構造で

は、そのリスト内の同じconsを持っているいくつかのリストが短所が変異されたときに、新しい値を取得する効果があります

変更可能な文字列のように、変更可能なオブジェクトの値がある場合は、consを変更する代わりに、それを直接変更することができます。

(let ((ele (copy-seq "ni"))) ; make ele mutable 
    (defparameter *x* (list ele 1 2 4)) 
    (defparameter *y* (list 5 6 ele 7))) 

*x* ; ==> ("ni" 1 2 4) 
*y* ; ==> (5 6 "ni" 7) 

;; mutating the string value, not the reference to the value 
(setf (aref (car *x*) 1) #\o) 

*x* ; ==> ("no" 1 2 4) 
*y* ; ==> (5 6 "no" 7) 

あなたはすべてのタイプのためにそれを一般化するボックスを作ることができます:あなたは本当に起こることボックスは、我々は変更する値を持つ構造であることで気づくけどとして

(defstruct box (data)) 

(let ((ele (make-box :data "ni"))) ; make ele mutable 
    (defparameter *x* (list ele 1 2 4)) 
    (defparameter *y* (list 5 6 ele 7))) 

*x* ; ==> (#s(box :data "ni") 1 2 4) 
*y* ; ==> (5 6 #s(box :data "ni") 7) 

;; mutating the string value, not the reference to the value 
(setf (box-data (car *x*)) 10) 

*x* ; ==> (#s(box :data 10) 1 2 4) 
*y* ; ==> (5 6 #s(box :data 10) 7) 

リストが指します同じ箱。

+0

リストの末尾はすべて共有されています。 2番目の要素だけを共有する必要がある場合は、変更可能なオブジェクトに値をラップする必要があります。 – jkiiski

+0

@jkiiskiもし、その値が変更可能な文字列のように変更できるのであれば、値を変更することはできますが、* shared structure *の場合は両方のリストに同じconsを持つ必要があります。しかし、テールである必要はありません。あなたは* y * '(list tmp tmp)'を作ることができ、最後は '((q 3)(q3))'となります。 – Sylwester

+0

はい、具体的な例では尾全体が共有されています。共有構造を必要とするのか、リストの真ん中に単一の共有オブジェクトしか持たないのかは、完全にはっきりしません。 – jkiiski

4

これは一般的に不可能です。

は、次の2つのリストを持っている想像:

CL-USER 11 > (sdraw (list 1 2 3)) 

[*|*]--->[*|*]--->[*|*]--->NIL 
|  |  | 
v  v  v 
1  2  3 

CL-USER 12 > (sdraw (list 'a 'b 'c)) 

[*|*]--->[*|*]--->[*|*]--->NIL 
|  |  | 
v  v  v 
A  B  C 

あなたはコンス・セルとの間のリンクを表しているだろうか?最初のリストの2番目のコンスセルのCARを変更すると、2番目のコンスセルのCARも変更されます。

一方の方法は、マシンの下に変更を検出し、何かをするメカニズムを持つことです。 Lispマシンではメモリの場所を見ることができますが、単純なCommon Lispではこれは不可能です。

また、CADRを変更して、マークされた特定のコンセルの変更を検出することができます。繰り返しますが、これは単純なCommon Lispではありません。したがって

? (setf l1 (list 1 2 3)) 
(1 2 3) 


? (watch (cdr l1)) 
(2 3) 

? (setf (cadr l1) 'a) 
> Error: Write to the CAR of watched cons cell (2 3) 
>  Faulting instruction: #<X86-DISASSEMBLED-INSTRUCTION (movq (% rsi) (@ 5 (% rdi))) #x302000E1EA0D> 
> While executing: CCL::SET-CADR, in process Listener(4). 

1がコンスセルが変更されたことを検出し、その変化に何かをするだろう条件ハンドラを呼び出すことができます。

エクスカーション

Clozure CLはコンスセルを監視する機構を備えています...

関連する問題