人力検索はてな
モバイル版を表示しています。PC版はこちら
i-mobile

Scheme(Gauche)についての質問です。

(define *alst* '((a b) (c d) (e f)))

上記で生成したリストに対し、

(let ((p (assoc 'e *alst*)))
(set! (cdr p) (cons 'x (cdr p))))

という処理を実行すると、*alst*が更新される("((a b) (c d) (e x f))"となる)のに対し、

(let ((p (cdr (assoc 'e *alst*))))
(set! p (cons 'y p)))

では*alst*が更新されないのはなぜでしょうか。

●質問者: hu2
●カテゴリ:コンピュータ
✍キーワード:Gauche Scheme SET リスト 更新
○ 状態 :終了
└ 回答数 : 2/2件

▽最新の回答へ

1 ● cinquanta
●35ポイント

まず、上の処理

(let ((p (assoc 'e *alst*)))

(set! (cdr p) (cons 'x (cdr p))))

について *alst* 内のリスト (e f) の部分の構造を考えてみます。

let により、変数 p に (e f) へのポインタが代入されます。これは次の図

p → ([A] . [B])
 ↓ ↓
 'e ([C] . [D])
 ↓
 'f

で表わすような構造の、対 ([A] . [B]) へのポインタとして考えることができます。

(car p) と (cdr p) はそれぞれ箱 [A] と [B] を表わしており、ここでは、[A] には 'e へのポインタ、[B] には対 ([C] . [D]) へのポインタが格納されています。

2行目の (set! (cdr p) (cons 'x (cdr p))) は、[B] の中身を、(cons 'x (cdr p)) に書き換えるということです。

(cons 'x (cdr p)) は、'x と、(cdr p) とで新たに対を作ります。

([E] . [F])
 ↓ ↓
 'x ([C] . [D])
 ↓
 'f

この新しい対へのポインタが set! により [B] に格納されます。

p → ([A] . [B])
 ↓ ↓
 'e ([E] . [F])
 ↓ ↓
 'x ([C] . [D])
 ↓
 'f

こうして *alst* が ((a b) (c d) (e x f)) に更新されます。

一方、下の処理

(let ((p (cdr (assoc 'e *alst*))))

(set! p (cons 'y p)))

では、初めに let によって次の図のように

 p
 ↓
([A] . [B])
 ↓ ↓
 'e ([C] . [D])
 ↓
 'f

変数 p の中身が箱 [B] へのポインタとなります。

次に (set! p (cons 'y p)) の部分ですが、まず cons によって新たな対 ('y . ([C] . [D])) が作られます。

([E] . [F])
 ↓ ↓
 'y ([C] . [D])
 ↓
 'f

そして、変数 p の中身をこの対へのポインタに書き換えます。

p → ([E] . [F])
 ↓ ↓
 'y ([C] . [D])
 ↓
 'f

ところが、もともと *alst* から参照されていた (e f) の部分、つまり対 ([A] . [B]) 以下の部分に対しては一度も書き換えが行われていないのです。

これが *alst* の変化しない理由です。

◎質問者からの返答

後者の場合、ポインタの指し示す先を書き換えるのではなく、

局所変数pを書き換えるのみということなので、*alst*は変化しないという事ですね。

よく理解できました。

非常に丁寧にご説明いただきありがとうございました。


2 ● pyopyopyo
●35ポイント

"let" は一時的なオブジェクトを作る関数です。C言語とかで言うところの、ローカル変数です。

ですので、たとえば

 (define X 1)
 (let ((tmp X)) 
 (set! tmp 2))

のように書いても、 X の値は1 のままです。何故なら、これは以下のように評価されるためです。

 変数Xに1を代入
 一時変数 tmp を用意して、値を変数Xにする
 一時変数 tmp の値を 2 に変更

同様に

 (let ((p (cdr (assoc 'e *alst*))))
 (set! p (cons 'y p)))

と書いてもこれは、一時変数 p の値が "(cons 'y p)" に変わるだけで *alst* の値は変わりません。

一方、元々の式

 (let ((p (assoc 'e *alst*)))
 (set! (cdr p) (cons 'x (cdr p))))

で *alst* の値が変わった理由は、 (set! ..) で (cdr p) を変更しているからです。

p自身は一時変数ですが、(cdr p) は *alst* の一部を指しているため *alst*の値が変わった、ということになります。


 (let ((p (cdr (assoc 'e *alst*))))
 (set! p (cons 'y p)))

 (let ((p (cdr (assoc 'e *alst*))))
 (set! (cdr p) (cons 'y p)))

などにすれば、 *alst* の値が変わります。試してみてください。

◎質問者からの返答

具体的な説明をいただきありがとうございました。

cinquantaさんの説明と併せて、ようやく理解できました。

関連質問


●質問をもっと探す●



0.人力検索はてなトップ
8.このページを友達に紹介
9.このページの先頭へ
対応機種一覧
お問い合わせ
ヘルプ/お知らせ
ログイン
無料ユーザー登録
はてなトップ