PFDS 8.2
8.2 Global Rebuilding
・基本的なアイデア : 再構成を通常の処理ごとに数回のステップでインクリメンタルに実行。
・再構成はコルーチンで行う。
・再構成された構造が必要になる時間までにはコルーチンが完了しておく必要がある。そのため、それに間に合うよう
にコルーチンを早く開始しなければならない。
・global rebuilding は2つの構造をやりくりすることで達成される。
  - プライマリ(working): 通常の構造。
すべてのクエリーと更新はプライマリ (working)上で行われる。
  - セカンダリ: 段階的に再構成される。
・パーシステンス:維持できる
・どの操作も高価でないので、任意の継続する操作は処理時間の上限に影響をおよぼさない。
8.2.1 Hood-Melville Real-Time Queue(1)
・(7.2のリアルタイムキューと同様に )キューの前と後ろを表現するのに 2つのリストを使う。
・後ろのリストが前のリストよりも 1つ長くなったとき、後ろのリストから前のリストに要素をインクリメンタルにローテートし
始める。
・RotationState
(1) Idle : 何もしない。
(2) Reversing Int [a] [a] [a] [a] : リバースしている状態。例 . Reversing 1 [2] [1,0] [4,3] [5,6]
(3) Appending Int [a] [a] : appendしている状態? 例. Appending 3 [2,1,0] [3,4,5,6]
(4) Done [a] : ローテーションが完了した状態。
8.2.1 Hood-Melville Real-Time Queue(2)
・invalidate
snocもしくはtailを呼ぶごとにexecを数回だけ呼ぶ
→ ローテーションが完了するときまでに、答えは正しくないかもしれない。特に、ローテーションの間に tailがk回
呼ばれる場合、結果のリストの最初の k個の要素は不正なものである。
→ この問題を解決する方法は 2つあるが、この本で採用されているのは (2)のほう。
(1) 不正な要素の数のカウントを維持し、 RotationStateに新しい状態 Deleting を追加し、不正な要素がなくなるま
でリストから一度に数個の要素を削除する。これは global rebuildingの定義に最もよくあてはまる
(2) 答えのリストに不正な要素を置くのを回避する。 f'にある正しい要素の数 (プログラムの中の ok)を保持し、こ
の数が0になると f' から r' に要素をコピーするのをやめる。ローテーションの間 tailを呼ぶときは常に、正しい要
素の数を減らす。
8.2.1 Hood-Melville Real-Time Queue(3)
・トリッキーな点
(1) ローテーションの間、キューの最初の数個は RotationStateの中のf'の後ろの方にある。どうやって headのクエリ
に答えるか → 古いworkingのフロントリストを維持する。ローテーションの間、 lenfのフィールドは、 workingのフロ
ントリスト fの長さではなく、作成中のリストの長さをカウントする。 → 次のページの snocの例を参照
(2) ローテーションについて。次のローテーションの始まる準備ができるか、もしくは workingのフロントのリストが
使い切られるまでにローテーションが完了していることを保証するために snoc と tailあたり、execを何回呼ばなけ
ればならないか (8.1の定数cに対応) → 次のローテーションは合計で 2m+2回の追加と削除の後に始まるが、フ
ロントのリストは m回の削除の後に使い切られてしまう (最初に、フロントリストの長さを m、リアリストの長さを m+1と
仮定)。
1処理につき2回execを呼ぶことにすれば、ローテーションは最大で m回の処理で完了する。
(3) exec IDLE = IDLEとなるような、IDLEをRotationStateに追加。
ローテーションの最中なのかそうでないのかということを心配することなく execを呼ぶことができる。
snoc
snoc (snoc (snoc (snoc (snoc (snoc (HM 0 [] Idle 0 []) 0) 1) 2) 3) 4) 5
⇒ HM 3 [0,1,2] Idle 3 [5,4,3]
これに6をsnoc : snoc (HM 3 [0,1,2] Idle 3 [5,4,3])
→ check 3 [0,1,2] Idle 4 [6,5,4,3]
→ lenr > lenfなので exec2 7 [0,1,2] Reversing 0 [0,1,2] [] [6,5,4,3] [] 0 []
exec (exec (state))
→ exec (exec (Reversing 0 [0,1,2] [] [6,5,4,3] [])) (exec 第1式を適用して下の行に移る )
→ exec (Reversing 1 [1,2] [0] [5,4,3] [6]) (exec 第1式を適用して下の行に移る )
→ Reversing 2 [2] [1,0] [4,3] [5,6]
したがって、
exec2 7 [0,1,2] Reversing 0 [0,1,2] [] [6,5,4,3] [] 0 []
 → HM 7※1 [0,1,2]※2 Reversing 2 [2] [1,0] [4,3] [5,6] 0 []
※1. HMの右隣の数 7 は作成中のリストの長さを示す (ローテーションの間、 lenfのフィールドは workingのフロン
トリストfの長さではなく、作成中のリストの長さを示す。 )
※2 古いworkingフロントリストの [0,1,2]を維持している。
tail
snoc (snoc (snoc (snoc (snoc (snoc (snoc (HM 0 [] Idle 0 []) 0) 1) 2) 3) 4) 5) 6
⇒ HM 7 [0,1,2] (Reversing 2 [2] [1,0] [4,3] [5,6]) 0 []
これにtailする: tail (HM 7 [0,1,2] (Reversing 2 [2] [1,0] [4,3] [5,6]) 0 [])
→ check 6 [1,2] (invalidate (Reversing 2 [2] [1,0] [4,3] [5,6]) 0 []) (invalidate 第1式適用)
→ check (6 [1,2] Reversing 1 [2] [1,0] [4,3] [5,6] 0 [])
→ exec2 (6 [1,2] Reversing 1 [2] [1,0] [4,3] [5,6] 0 []) (lenr <= lenf なので)
exec (exec (Reversing 1 [2] [1,0] [4,3] [5,6]))
(exec 第1式を適用して下の行へ移る )
→ exec (Reversing 2 [] [2,1,0] [3] [4,5,6]) (exec 第2式を適用して下の行へ移る )
→ Appending 2 [2,1,0] [3,4,5,6]
exec2 (6 [1,2] Reversing 1 [2] [1,0] [4,3] [5,6] 0 [])
→ HM 6 [1,2] (Appending 2 [2,1,0] [3,4,5,6]) 0 []
 ※ ここで、fの中の 0 は不正な値。これは Appendingの右隣の数 2 から判断できる

Purely functional data structures 8.2 日本語での説明

  • 1.
  • 2.
    8.2 Global Rebuilding ・基本的なアイデア: 再構成を通常の処理ごとに数回のステップでインクリメンタルに実行。 ・再構成はコルーチンで行う。 ・再構成された構造が必要になる時間までにはコルーチンが完了しておく必要がある。そのため、それに間に合うよう にコルーチンを早く開始しなければならない。 ・global rebuilding は2つの構造をやりくりすることで達成される。   - プライマリ(working): 通常の構造。 すべてのクエリーと更新はプライマリ (working)上で行われる。   - セカンダリ: 段階的に再構成される。 ・パーシステンス:維持できる ・どの操作も高価でないので、任意の継続する操作は処理時間の上限に影響をおよぼさない。
  • 3.
    8.2.1 Hood-Melville Real-TimeQueue(1) ・(7.2のリアルタイムキューと同様に )キューの前と後ろを表現するのに 2つのリストを使う。 ・後ろのリストが前のリストよりも 1つ長くなったとき、後ろのリストから前のリストに要素をインクリメンタルにローテートし 始める。 ・RotationState (1) Idle : 何もしない。 (2) Reversing Int [a] [a] [a] [a] : リバースしている状態。例 . Reversing 1 [2] [1,0] [4,3] [5,6] (3) Appending Int [a] [a] : appendしている状態? 例. Appending 3 [2,1,0] [3,4,5,6] (4) Done [a] : ローテーションが完了した状態。
  • 4.
    8.2.1 Hood-Melville Real-TimeQueue(2) ・invalidate snocもしくはtailを呼ぶごとにexecを数回だけ呼ぶ → ローテーションが完了するときまでに、答えは正しくないかもしれない。特に、ローテーションの間に tailがk回 呼ばれる場合、結果のリストの最初の k個の要素は不正なものである。 → この問題を解決する方法は 2つあるが、この本で採用されているのは (2)のほう。 (1) 不正な要素の数のカウントを維持し、 RotationStateに新しい状態 Deleting を追加し、不正な要素がなくなるま でリストから一度に数個の要素を削除する。これは global rebuildingの定義に最もよくあてはまる (2) 答えのリストに不正な要素を置くのを回避する。 f'にある正しい要素の数 (プログラムの中の ok)を保持し、こ の数が0になると f' から r' に要素をコピーするのをやめる。ローテーションの間 tailを呼ぶときは常に、正しい要 素の数を減らす。
  • 5.
    8.2.1 Hood-Melville Real-TimeQueue(3) ・トリッキーな点 (1) ローテーションの間、キューの最初の数個は RotationStateの中のf'の後ろの方にある。どうやって headのクエリ に答えるか → 古いworkingのフロントリストを維持する。ローテーションの間、 lenfのフィールドは、 workingのフロ ントリスト fの長さではなく、作成中のリストの長さをカウントする。 → 次のページの snocの例を参照 (2) ローテーションについて。次のローテーションの始まる準備ができるか、もしくは workingのフロントのリストが 使い切られるまでにローテーションが完了していることを保証するために snoc と tailあたり、execを何回呼ばなけ ればならないか (8.1の定数cに対応) → 次のローテーションは合計で 2m+2回の追加と削除の後に始まるが、フ ロントのリストは m回の削除の後に使い切られてしまう (最初に、フロントリストの長さを m、リアリストの長さを m+1と 仮定)。 1処理につき2回execを呼ぶことにすれば、ローテーションは最大で m回の処理で完了する。 (3) exec IDLE = IDLEとなるような、IDLEをRotationStateに追加。 ローテーションの最中なのかそうでないのかということを心配することなく execを呼ぶことができる。
  • 6.
    snoc snoc (snoc (snoc(snoc (snoc (snoc (HM 0 [] Idle 0 []) 0) 1) 2) 3) 4) 5 ⇒ HM 3 [0,1,2] Idle 3 [5,4,3] これに6をsnoc : snoc (HM 3 [0,1,2] Idle 3 [5,4,3]) → check 3 [0,1,2] Idle 4 [6,5,4,3] → lenr > lenfなので exec2 7 [0,1,2] Reversing 0 [0,1,2] [] [6,5,4,3] [] 0 [] exec (exec (state)) → exec (exec (Reversing 0 [0,1,2] [] [6,5,4,3] [])) (exec 第1式を適用して下の行に移る ) → exec (Reversing 1 [1,2] [0] [5,4,3] [6]) (exec 第1式を適用して下の行に移る ) → Reversing 2 [2] [1,0] [4,3] [5,6] したがって、 exec2 7 [0,1,2] Reversing 0 [0,1,2] [] [6,5,4,3] [] 0 []  → HM 7※1 [0,1,2]※2 Reversing 2 [2] [1,0] [4,3] [5,6] 0 [] ※1. HMの右隣の数 7 は作成中のリストの長さを示す (ローテーションの間、 lenfのフィールドは workingのフロン トリストfの長さではなく、作成中のリストの長さを示す。 ) ※2 古いworkingフロントリストの [0,1,2]を維持している。
  • 7.
    tail snoc (snoc (snoc(snoc (snoc (snoc (snoc (HM 0 [] Idle 0 []) 0) 1) 2) 3) 4) 5) 6 ⇒ HM 7 [0,1,2] (Reversing 2 [2] [1,0] [4,3] [5,6]) 0 [] これにtailする: tail (HM 7 [0,1,2] (Reversing 2 [2] [1,0] [4,3] [5,6]) 0 []) → check 6 [1,2] (invalidate (Reversing 2 [2] [1,0] [4,3] [5,6]) 0 []) (invalidate 第1式適用) → check (6 [1,2] Reversing 1 [2] [1,0] [4,3] [5,6] 0 []) → exec2 (6 [1,2] Reversing 1 [2] [1,0] [4,3] [5,6] 0 []) (lenr <= lenf なので) exec (exec (Reversing 1 [2] [1,0] [4,3] [5,6])) (exec 第1式を適用して下の行へ移る ) → exec (Reversing 2 [] [2,1,0] [3] [4,5,6]) (exec 第2式を適用して下の行へ移る ) → Appending 2 [2,1,0] [3,4,5,6] exec2 (6 [1,2] Reversing 1 [2] [1,0] [4,3] [5,6] 0 []) → HM 6 [1,2] (Appending 2 [2,1,0] [3,4,5,6]) 0 []  ※ ここで、fの中の 0 は不正な値。これは Appendingの右隣の数 2 から判断できる