SICP 読み (278) 5.3 記憶の割り当てとごみ集め

昨晩、力尽きてるなぁ。どこまでヤッたんでしょうか (ため息
てーか、自分で見返してみても意味が分かりにくい。通過したラベルと属性値だけ列挙してみます。微妙なエントリになる可能性大。
イチからリトライ、って昨晩と同じトコで力尽きたら笑うな。まずは最初の状態。

gosh> the-cars 
(p4 n3 x n4 n1 x n2)
gosh> the-cdrs 
(p1 p3 x e0 p6 x e0)
gosh> new-cars
()
gosh> new-cdrs
()

で、begin-garbage-collection が済んだ時点でのレジスタの値は以下。

  • free : 0
  • scan : 0
  • old : 0 (root)
  • new : - (最初の assign は pair な手続き)
  • cont : reassign-root

この後

  • relocate-old-result-in-new
  • pair
  • reassign-root

という手続きを経て以下の状態に (このあたりは昨晩エントリな書き直し)

gosh> the-cars
(b n3 x n4 n1 x n2)
gosh> the-cdrs
(p0 p3 x e0 p6 x e0)
gosh> new-cars
(p4)
gosh> new-cdrs
(p1)
gosh>

あるいはレジスタは以下。

  • free : 1
  • scan : 0
  • old : 0 (root)
  • new : 0
  • cont : reassign-root

次に jmp するのは gc-loop になる。あと、root レジスタの値は new-{car, cdr} の先頭って思ってて良いのかな。gc-loop な処理が終了した時点では *-c?r は変化ナシ。レジスタの値は以下になっているはず。

  • free : 1
  • scan : 0
  • old : 4 (the-c?r の p4)
  • new : 0
  • cont : update-car

  • relocate-old-result-in-new
  • pair

の後には update-car に jmp するんですが、jmp 直前の *-c?r の状態が以下。

gosh> the-cars
(b n3 x n4 b x n2)
gosh> the-cdrs
(p0 p3 x e0 p1 x e0)
gosh> new-cars
(p4 n1)
gosh> new-cdrs
(p1 p6)
gosh>

あるいはレジスタは以下。

  • free : 2
  • scan : 0
  • old : 4 (the-c?r の p4)
  • new : 1 (new-c?r の p1)
  • cont : update-car

これで update-car に jmp して一連の手続きが終わったら

gosh> the-cars
(b n3 x n4 b x n2)
gosh> the-cdrs
(p0 p3 x e0 p1 x e0)
gosh> new-cars
(p1 n1)
gosh> new-cdrs
(p1 p6)
gosh>

あるいはレジスタが以下。

  • free : 2
  • scan : 0
  • old : 1 (the-c?r の p1)
  • new : 1 (new-c?r の p1)
  • cont : update-cdr

この後は

  • relocate-old-result-in-new
  • pair

な命令が実行された後に update-cdr に jmp しますが、jmp 直前の状態が以下。

gosh> the-cars
(b b x n4 b x n2)
gosh> the-cdrs
(p0 p2 x e0 p1 x e0)
gosh> new-cars
(p1 n1 n3)
gosh> new-cdrs
(p1 p6 p3)
gosh>

レジスタは以下

  • free : 3
  • scan : 0
  • old : 1 (the-c?r の p1)
  • new : 2 (new-c?r の p2)
  • cont : update-cdr

この状態から update-cdr した直後の状態は以下になる。

gosh> the-cars
(b b x n4 b x n2)
gosh> the-cdrs
(p0 p2 x e0 p1 x e0)
gosh> new-cars
(p1 n1 n3)
gosh> new-cdrs
(p2 p6 p3)
gosh>

レジスタは以下

  • free : 3
  • scan : 1
  • old : 1 (the-c?r の p1)
  • new : 2 (new-c?r の p2)
  • cont : update-cdr

これで new-* の最初の要素が完全な状態に。これで gc-loop に jmp らしい。gc-loop 終了時のレジスタの内容が以下。

  • free : 3
  • scan : 1
  • old : n1
  • new : 2 (new-c?r の p2)
  • cont : update-car

これで relocate-old-result-in-new に jmp するんですが、これは pair に branch しないパターンだな。relocate (略) の終了時のレジスタの状態が以下

  • free : 3
  • scan : 1
  • old : n1
  • new : n1
  • cont : update-car

で、update-car に jmp ッスか??
って同じ値 (取り出したものを再度セットしている) なのか。このあたりは微妙なハンドル入ってそうでいやらしいなぁ。(何
ええと pair の場合の update-car は new-car の要素のアドレスで書き換えなんだけど、pair じゃない場合は new に現在の値を入れとく訳か。update-car 終了時点のレジスタの状態 (*-c?r は変更ないはずなので略) は以下

  • free : 3
  • scan : 1
  • old : p6 (the-c?r の p6)
  • new : n1
  • cont : update-cdr

で、relocate-(略) に jmp して pair 終了時点のナニが以下

gosh> the-cars
(b b x n4 b x b)
gosh> the-cdrs
(p0 p2 x e0 p1 x p3)
gosh> new-cars
(p1 n1 n3 n2)
gosh> new-cdrs
(p2 p6 p3 e0)
gosh>

レジスタが以下

  • free : 4
  • scan : 1
  • old : p6 (the-c?r の p6)
  • new : p3 (new-c?r の p3)
  • cont : update-cdr

で、update-cdr なんですが終了時点の状態が以下になる、と

gosh> the-cars
(b b x n4 b x b)
gosh> the-cdrs
(p0 p2 x e0 p1 x p3)
gosh> new-cars
(p1 n1 n3 n2)
gosh> new-cdrs
(p2 p3 p3 e0)
gosh>

レジスタが以下

  • free : 4
  • scan : 2
  • old : p6 (the-c?r の p6)
  • new : p3 (new-c?r の p3)
  • cont : update-cdr

これで再度 gc-loop に戻る訳ですか。なんとなく順調。gc-loop 終了時点のレジスタの状態は以下になっているはず

  • free : 4
  • scan : 2
  • old : n3
  • new : p3 (new-c?r の p3)
  • cont : update-car

で、relocate (略) では old が pair でないので new も n3 になって update-car 終了時点のナニが以下になるはず (*-c?r は変更されないので略)

  • free : 4
  • scan : 2
  • old : p3 (the-c?r の p3)
  • new : p3 (new-c?r の p3)
  • cont : update-cdr

なんとなくスッ飛ばしてしまいそうな感じがする。気を付けて追い掛ける。ここから relocate-(略) に jmp するんですが、pair 終了時の状態は以下のはず。

gosh> the-cars
(b b x b b x b)
gosh> the-cdrs
(p0 p2 x p4 p1 x p3)
gosh> new-cars
(p1 n1 n3 n2 n4)
gosh> new-cdrs
(p2 p3 p3 e0 e0)
gosh>

レジスタが以下

  • free : 5
  • scan : 2
  • old : p3 (the-c?r の p3)
  • new : p4 (new-c?r の p4)
  • cont : update-cdr

これで全ての要素は移動できましたが、new-* のポインタがまだ微妙。ここで update-cdr したらほぼ終了なのかな。update-cdr 終了時点のソレは以下

gosh> the-cars
(b b x b b x b)
gosh> the-cdrs
(p0 p2 x p4 p1 x p3)
gosh> new-cars
(p1 n1 n3 n2 n4)
gosh> new-cdrs
(p2 p3 p4 e0 e0)
gosh>

レジスタが以下

  • free : 5
  • scan : 3
  • old : p3 (the-c?r の p3)
  • new : p4 (new-c?r の p4)
  • cont : update-cdr

で、gc-loop に戻る。終了時点のレジスタの状態が以下

  • free : 5
  • scan : 3
  • old : n2
  • new : p4 (new-c?r の p4)
  • cont : update-car

で、これ以降は car も cdr も pair ではないのでさくさく進んで scan が 5 になって終了、ですね。良かった良かった。

失恋対がある場合

もやっとこ。まず *-c?r な初期値を以下としてみます。

gosh> the-cars 
(p5 p5 n3 x n4 n1 x n2)
gosh> the-cdrs 
(p1 p2 p4 x e0 p7 x e0)
gosh> new-cars
()
gosh> new-cdrs
()

これはこんな状態だと思ってるんですが良いのだろうか。

gosh> (define l1 '(1 2))
l1
gosh> (define l2 '(() 3 4))
l2
gosh> (set-car! l2 l1)
#<undef>
gosh> (define l '(() ()))
l
gosh> (set-car! l l1)
#<undef>
gosh> (set-cdr! l l2)
#<undef>
gosh> l
(#0=(1 2) #0# 3 4)
gosh>

なんか違う気がする。とりあえず正誤はスルーとして順に見ていきます。レジスタの初期値はさっきと同じで良いはず。

  • free : 0
  • scan : 0
  • old : 0 (root)
  • new : - (最初の assign は pair な手続き)
  • cont : reassign-root

この状態から relocate-(略) に突入。pair 終了時にどうなるかというと以下。

gosh> the-cars 
(b p5 n3 x n4 n1 x n2)
gosh> the-cdrs 
(p0 p2 p4 x e0 p7 x e0)
gosh> new-cars
(p5)
gosh> new-cdrs
(p1)

以下がレジスタ

  • free : 1
  • scan : 0
  • old : p0 (the-c?r の p0)
  • new : p0 (new-c?r の p0)
  • cont : reassign-root

大丈夫かな。で、gc-loop の終了時点なレジスタが以下

  • free : 1
  • scan : 0
  • old : p5 (the-c?r の p5)
  • new : p0 (new-c?r の p0)
  • cont : update-car

で、relocate-(略) に jmp して pair 終了時点なソレが以下

gosh> the-cars 
(b p5 n3 x n4 b x n2)
gosh> the-cdrs 
(p0 p2 p4 x e0 p1 x e0)
gosh> new-cars
(p5 n1)
gosh> new-cdrs
(p1 p7)

以下がレジスタ

  • free : 2
  • scan : 0
  • old : p5 (the-c?r の p5)
  • new : p1 (new-c?r の p1)
  • cont : update-car

次の update-car 終了時点の状態が以下

gosh> the-cars 
(b p5 n3 x n4 b x n2)
gosh> the-cdrs 
(p0 p2 p4 x e0 p1 x e0)
gosh> new-cars
(p1 n1)
gosh> new-cdrs
(p1 p7)

以下がレジスタ

  • free : 2
  • scan : 0
  • old : p1 (the-c?r の p1)
  • new : p1 (new-c?r の p1)
  • cont : update-cdr

で、再度 relocate-(略) に jmp して pair 終了時点の状態が以下。ここで失恋対が登場するはず。ただ、ここでは単純にコピるだけなのか。

gosh> the-cars 
(b b n3 x n4 b x n2)
gosh> the-cdrs 
(p0 p2 p4 x e0 p1 x e0)
gosh> new-cars
(p1 n1 p5)
gosh> new-cdrs
(p1 p7 p2)

以下がレジスタ

  • free : 3
  • scan : 0
  • old : p1 (the-c?r の p1)
  • new : p2 (new-c?r の p2)
  • cont : update-cdr

で、update-cdr 終了時の状態が以下

gosh> the-cars 
(b b n3 x n4 b x n2)
gosh> the-cdrs 
(p0 p2 p4 x e0 p1 x e0)
gosh> new-cars
(p1 n1 p5)
gosh> new-cdrs
(p2 p7 p2)

以下がレジスタ

  • free : 3
  • scan : 1
  • old : p1 (the-c?r の p1)
  • new : p2 (new-c?r の p2)
  • cont : update-cdr

失恋対な処理に行くまでもう少しかかるな。これで gc-loop に jmp して終了時点のレジスタの状態が以下

  • free : 3
  • scan : 1
  • old : n1
  • new : p2 (new-c?r の p2)
  • cont : update-car

ちょっとスルーして update-car 終了時のレジスタの状態が以下になる (*-c?r は変わらない)

  • free : 3
  • scan : 1
  • old : p7 (the-c?r の p7)
  • new : p2 (new-c?r の p2)
  • cont : update-cdr

げ、p7 かよ、と言いつつ relocate-(略) 方面に去って pair 終了時の状態が以下

gosh> the-cars 
(b b n3 x n4 b x n2)
gosh> the-cdrs 
(p0 p2 p4 x e0 p1 x e0)
gosh> new-cars
(p1 n1 p5 n2)
gosh> new-cdrs
(p2 p7 p2 e0)

以下がレジスタ

  • free : 4
  • scan : 1
  • old : p7 (the-c?r の p7)
  • new : p3 (new-c?r の p3)
  • cont : update-cdr

で、update-cdr 終了後は以下になる

gosh> the-cars 
(b b n3 x n4 b x n2)
gosh> the-cdrs 
(p0 p2 p4 x e0 p1 x e0)
gosh> new-cars
(p1 n1 p5 n2)
gosh> new-cdrs
(p2 p3 p2 e0)

以下がレジスタ

  • free : 4
  • scan : 2
  • old : p7 (the-c?r の p7)
  • new : p3 (new-c?r の p3)
  • cont : update-cdr

やれやれ、と言いつつ gc-loop に戻って終了した瞬間のレジスタが以下

  • free : 4
  • scan : 2
  • old : p5 (the-c?r の p5)
  • new : p3 (new-c?r の p3)
  • cont : update-car

を、ようやくキたな。relocate-(略) 方面に去るんですが、broken-heart は pair 認定で良いよな。already-moved が終わった時点でのレジスタの状態が以下

  • free : 4
  • scan : 2
  • old : p5 (the-c?r の p5)
  • new : p1 (new-c?r の p1)
  • cont : update-car

で、update-car に jmp して終わったらどうなるか、というと

gosh> the-cars 
(b b n3 x n4 b x n2)
gosh> the-cdrs 
(p0 p2 p4 x e0 p1 x e0)
gosh> new-cars
(p1 n1 p1 n2)
gosh> new-cdrs
(p2 p3 p2 e0)
  • free : 4
  • scan : 2
  • old : p2 (the-c?r の p2)
  • new : p1 (new-c?r の p1)
  • cont : update-cdr

で、再度 relocate-(略) 方面に去った後のナニが以下

gosh> the-cars 
(b b b x n4 b x n2)
gosh> the-cdrs 
(p0 p2 p4 x e0 p1 x e0)
gosh> new-cars
(p1 n1 p1 n2 n3)
gosh> new-cdrs
(p2 p3 p2 e0 p4)
  • free : 5
  • scan : 2
  • old : p2 (the-c?r の p2)
  • new : p4 (new-c?r の p4)
  • cont : update-cdr

で、update-cdr で new-cdr の p2 を p4 にして scan が加算で以下

gosh> the-cars 
(b b b x n4 b x n2)
gosh> the-cdrs 
(p0 p2 p4 x e0 p1 x e0)
gosh> new-cars
(p1 n1 p1 n2 n3)
gosh> new-cdrs
(p2 p3 p4 e0 p4)
  • free : 5
  • scan : 3
  • old : p2 (the-c?r の p2)
  • new : p4 (new-c?r の p4)
  • cont : update-cdr

ここから先はスルーで良いかな。ちょっとフン詰まりがヌケてすっきり感満点って感じがします。わははは。

蛇足

都合二時間の現実トウヒでありました。ちょっとヤリ杉。