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*が更新されないのはなぜでしょうか。

回答の条件
  • 1人2回まで
  • 登録:
  • 終了:2009/07/25 09:20:02
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

回答2件)

id:cinquanta No.1

回答回数51ベストアンサー獲得回数4

ポイント35pt

まず、上の処理

(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* の変化しない理由です。

id:hu2

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

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

よく理解できました。

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

2009/07/19 09:12:04
id:pyopyopyo No.2

回答回数377ベストアンサー獲得回数98

ポイント35pt

"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* の値が変わります。試してみてください。

id:hu2

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

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

2009/07/19 09:13:37

コメントはまだありません

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません