2

私は、Scheme とリストを使用する私のプロジェクトの 1 つで、このやや奇妙な動作に気付きました。動作を 1 つのセクションに分離することができました。コードは次のとおりです。

(define x (list 1 2 3))
(define y (list 4 5))
(define z (cons (car x) (cdr y)))
(define w (append y z))
(define v (cons (cdr x) (cdr y)))
(set-car! x 6)
(set-car! y 7)
(set-cdr! (cdr x) (list 8))

x
y
z
w
v

次の出力が得られます。

(6 2 8)
(7 5)
(1 5)
(4 5 1 5)
((2 8) 5)

誰でも私に説明できますか:

  1. なぜ(set-car! x 6)更新しないのZですか?私の理解によると、対応する値へのポインターまたは参照を返しますcarcdrこれは本当に奇妙で、私はちょっと混乱しています。
  2. car/cdrが参照/ポインターを返さない場合、最終set-cdr!的にリストを操作する方法はv?

何か案は?これは単純な修正ですが、なぜ変数の奇妙なことが起こっているのかについてもっと興味があります。

4

4 に答える 4

22

では、プログラムを 1 行ずつ見ていきましょう。また、新しく作成されたオブジェクトごとに一意の番号 (C のような言語に慣れている場合は、オブジェクト アドレスと考えてください) を割り当てているので、何が何であるかがわかります。:-)

(define x (list 1 2 3))             ; => #1 = (1 . #2), #2 = (2 . #3), #3 = (3 . ())
(define y (list 4 5))               ; => #4 = (4 . #5), #5 = (5 . ())
(define z (cons (car x) (cdr y)))   ; => #6 = (1 . #5)
(define w (append y z))             ; => #7 = (4 . #8), #8 = (5 . #6)
(define v (cons (cdr x) (cdr y)))   ; => #9 = (#2 . #5)
(set-car! x 6)                      ; => #1 = (6 . #2)
(set-car! y 7)                      ; => #4 = (7 . #5)
(set-cdr! (cdr x) (list 8))         ; => #2 = (2 . #10), #10 = (8 . ())

次に、値を見てみましょう (参照ごとに、最後に割り当てられた値を使用します)。

x   ; #1 => (6 . #2) => (6 . (2 . #10)) => (6 2 8)
y   ; #4 => (7 . #5) => (7 5)
z   ; #6 => (1 . #5) => (1 5)
w   ; #7 => (4 . #8) => (4 . (5 . #6)) => (4 . (5 . (1 . #5))) => (4 5 1 5)
v   ; #9 => (#2 . #5) => ((2 . #10) 5) => ((2 8) 5)

編集:コメントに図を含めることはできないため、回答を説明するために図を追加しています。上記の値を示す図を作成する時間はありませんが、これでいくつかのことが説明できることを願っています。

式ツリー

各ペアには 2 つの「スロット」 と がcarありcdr、上の図の左右のボックスとして表されています。ご覧のとおり、これらの各スロットには次の 3 つの可能性があります。

  1. アトム (例では数字、または図では 、 、 などlets5記号sqrt)
  2. 参照 (図では矢印で表されています)
  3. Null (図ではブラック ボックスとして表されています)

これらのいずれかを任意のスロットに配置できます。したがって、上記の説明では、各#アイテムは矢印であり、非#数値はそれぞれアトムであり、それぞれが()ブラック ボックスです。だから、ラインで

(define v (cons (cdr x) (cdr y)))

ペアを作成していて、左側のスロットには右側のスロットと同じ内容がありx(つまり、ペア 2 に向かう矢印)、右側のスロットには右側のスロットと同じ内容があります。のy(ペア 5 に向かう矢印)。言い換えると、 の両方のボックスにv矢印が含まれており、それぞれが異なるペアから離れています。

これがより理にかなっていることを願っています。:-)

于 2009-04-21T02:14:06.610 に答える
4

(define z (cons (car x) (cdr y))) は、z の先頭に新しいコンス セルを割り当てました。x の先頭は z の先頭とは異なるコンス セルであるため、このコンス セルの x の内容を変更しても z は変更されません。

于 2009-04-21T02:09:10.927 に答える
2

各変数はリストを保持するのではなく、consセルのみを保持することに注意してください。リスト (またはツリー) は、いくつかのコンス セルの複合構造であり、その一部は複数の構造間で共有される場合があります。

また、不変の値 (数値やシンボルなど) または可変の値 (cons や文字列など) のいずれかである値について考える代わりに、ポインタと参照について考えています。可変の値は割り当てられ、ガベージ コレクションされ、参照によって渡されます.

覚えておくべき最後のポイント:consプロシージャは常に新しいコンス セルを割り当てます。

の後(define z (cons (car x) (cdr y)))、z は、x と同じ、x と同じ車、および y と同じ cdr を持つ真新しいコンスを保持します。ときは(set-car! x)、z ではなく x コンスセルを変更するだけです。

の後(define v (cons (cdr x) (cdr y)))、v は car が x の cdr と同じ値である真新しいコンスを保持します。それがコンスセルです。 (cdr x)とまったく同じコンスセルです。2 つのリストで共有されます。その共有コンスセルが によって変更される(set-cdr! (cdr x) ...)と、両方のリストが影響を受けます。

于 2009-04-21T03:02:22.937 に答える
0

ボックスとポインターの図は、@chris が作成した例を示しています。

(define x (list 1 2 3))             ; => #1 = (1 . #2), #2 = (2 . #3), #3 = (3 . ())
(define y (list 4 5))               ; => #4 = (4 . #5), #5 = (5 . ())
(define z (cons (car x) (cdr y)))   ; => #6 = (1 . #5)
(define w (append y z))             ; => #7 = (4 . #8), #8 = (5 . #6)
(define v (cons (cdr x) (cdr y)))   ; => #9 = (#2 . #5)
(set-car! x 6)                      ; => #1 = (6 . #2)
(set-car! y 7)                      ; => #4 = (7 . #5)
(set-cdr! (cdr x) (list 8))         ; => #2 = (2 . #10), #10 = (8 . ())

ここに画像の説明を入力

于 2014-03-05T20:14:19.377 に答える