Mercurial > hg > Papers > 2014 > toma-master
annotate paper/chapter3.tex @ 60:79d168016df4
add memorize
author | Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp> |
---|---|
date | Tue, 11 Feb 2014 22:58:43 +0900 |
parents | 0a8d66c9ccd1 |
children | d11f4c6c7657 |
rev | line source |
---|---|
43 | 1 \chapter{Haskellによる\\並列データベースの実装}\label{ch:impl} |
48 | 2 本章では, 並列データベース Jungle の実装について述べる. |
10 | 3 |
4 \section{木構造データベース Jungle} | |
47 | 5 非破壊的木構造データベース Jungle は, Haskell で実装された並列データベースである. |
6 非破壊的木構造の方法に則った関数を提供する. | |
23 | 7 |
47 | 8 % 本研究では, HTTP サーバ Warp と組み合わせて掲示板システムとして利用しているが, 他のシステムに組み込むことも可能である. |
9 Jungle の基本的な使い方の手順について説明する. | |
23 | 10 \begin{enumerate} |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
11 \item{木構造を保持する Jungle を作成する} |
23 | 12 \item{Jungle 内に新しい木を名前をつけて作成する} |
47 | 13 \item{木の名前を用いて, ルートノードの取得を行い, データを参照する} |
14 \item{もしくは, 木の名前を用いて, ルートノードの更新を行う} | |
23 | 15 \end{enumerate} |
16 | |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
17 \subsubsection{Jungle が持つデータ型} |
49 | 18 非破壊的木構造データベース Jungle が持つのデータ型を表\ref{tab:components}に表す. |
60 | 19 また, データ型内部のデータ構造を表\ref{tab:components2}に表す. |
49 | 20 |
47 | 21 木構造の集まりを表現する Jungle, 単体の木構造を表現する Tree がある. |
22 Node は子と属性を任意の数持てる. | |
49 | 23 データ型として定義することで, 内部の型の整合性が保たれる. |
24 例えば, Node の1つ目の型は (Map Int Node) となり他の型は許されない. | |
25 非破壊的木構造データベース Jungle のデータ型について, ひとつずつ説明する. | |
35 | 26 |
34 | 27 \begin{table}[!htbp] |
28 \begin{center} | |
48 | 29 \begin{tabular}{|c||c|c|} \hline |
30 型名 & 概要 \\ \hline | |
31 Jungle & 木の作成・取得を担当する. \\ \hline | |
32 Tree & 木の名前とルートノードの情報を保持している. \\ \hline | |
33 Node & 基本的なデータ構造, 子と属性を任意の数持てる. \\ \hline | |
34 | 34 \end{tabular} |
35 \end{center} | |
60 | 36 \label{tab:components} |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
37 \caption{Jungle が持つデータ型} |
34 | 38 \end{table} |
39 | |
60 | 40 \newpage |
41 \begin{table}[htbp] | |
48 | 42 \begin{center} |
43 \begin{tabular}{|c||c|} \hline | |
44 型名 & データ構造 \\ \hline | |
45 Jungle & Jungle (TVar (Map String Tree)) \\ \hline | |
46 Tree & Tree (TVar Node) String \\ \hline | |
47 Node & Node (Map Int Node) (Map String ByteString) \\ \hline | |
48 \end{tabular} | |
49 \end{center} | |
60 | 50 \label{tab:components2} |
48 | 51 \caption{データ型のデータ構造} |
52 \end{table} | |
53 | |
60 | 54 \clearpage |
55 \section{Jungle} | |
47 | 56 Jungle は木構造の集まりを表現する. |
49 | 57 木には名前がついており, Tree の情報と一緒に保持している(ソースコード\ref{src:jungle}). |
35 | 58 |
49 | 59 \begin{lstlisting}[label=src:jungle, caption=Jungleのデータ型の定義] |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
60 data Jungle = Jungle { getJungleMap :: (TVar (Map String Tree)) } |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
61 \end{lstlisting} |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
62 |
47 | 63 Jungle のデータ構造は, Jungle (TVar (Map String Tree)) である. |
64 getJungleMap :: というのは, Haskell のレコード構文である. | |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
65 |
47 | 66 レコード構文は, データ構造へのアクセサを提供する. |
49 | 67 getJungleMap は関数で, ソースコード\ref{src:getjunglemap}の型を持つ. |
47 | 68 これは, Jungleを受け取って, TVar (Map String Tree)を返す関数である. |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
69 |
47 | 70 レコード構文はデータ型を受け取って, :: の右側の型の値を取り出せる関数を作成すると思えば良い. |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
71 |
49 | 72 \begin{lstlisting}[label=src:getjunglemap, caption=getJungleMap] |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
73 getJungleMap :: Jungle -> TVar (Map String Tree) |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
74 \end{lstlisting} |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
75 |
47 | 76 Jungle の木の取り扱いには, Haskell の Data.Map を利用している. |
77 型名は, Map である. | |
78 Map は, 連想配列を扱うことのできるデータ構造である. | |
79 平衡木を用いて, 挿入や参照が O (log n)で済むように設計されている. | |
60 | 80 Data.Mapを理解するためにはリストで考えると分かりやすい(ソースコード\ref{src:map_list}). |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
81 |
60 | 82 \begin{lstlisting}[label=src:map_list, caption=リストで定義した場合のMap] |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
83 data Map k a = Map [(k,a)] |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
84 |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
85 lookup' :: Eq k => k -> Map k a -> Maybe a |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
86 lookup' k (Map []) = Nothing |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
87 lookup' k (Map ((k',a):xs)) = if k == k' |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
88 then Just a |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
89 else lookup k xs |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
90 |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
91 |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
92 insert :: k -> a -> Map k a -> Map k a |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
93 insert k a (Map x) = Map ((k,a):x) |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
94 |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
95 test = Map [("key","value"),("fizz","buzz")] |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
96 \end{lstlisting} |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
97 |
47 | 98 Map は, キーと値のペアのリストだと考えることができる. |
99 キーが一致する値を探す場合, lookup'を用いる. | |
100 Maybe モナドを用いて, データがなければ Nothing, データがあれば Just に包んで返す. | |
101 $=>$ の前にある, Eq kは, 型クラスの制約である. | |
102 内部で k と k' の同値性をテストしているため, k は同値性をチェックできる型クラス Eq に属している型である必要がある. | |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
103 |
47 | 104 新たにキーと値のペアを, Mapに追加するには insertを用いる. |
105 Haskell では, 受け取った引数を変更することができないため, ペアを追加した新しい Map を返す. | |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
106 |
47 | 107 木の取り扱いには Haskell のソフトウェア・トランザクショナル・メモリ (STM) を利用して状態を持たせ, スレッド間で共有できるようにしてある. |
108 これは, 各スレッドから木構造を新たに作成できるようにするためである. | |
109 STM は, スレッド間でデータを共有するためのツールである. STM を利用することでロック忘れによる競合状態や, デッドロックといった問題から解放される. | |
110 Jungle のデータ構造の Map の前に付いている TVar というのは, Transactional variablesの略で, STM で管理する変数に対して利用する. | |
34 | 111 |
23 | 112 \subsubsection{Jungle と木の作成} |
49 | 113 Jungle は, 複数の非破壊的木構造を持つため, Map で木を管理している(図\ref{fig:jungle}). |
114 Tree には名前がついており, 複数のバージョンの Tree のノードのどれが最新かという情報を持っている. | |
10 | 115 |
23 | 116 \begin{figure}[!htbp] |
117 \begin{center} | |
49 | 118 \includegraphics[scale=0.7]{./images/jungle_type.pdf} |
23 | 119 \end{center} |
120 \caption{複数の木を扱えるJungle} | |
121 \label{fig:jungle} | |
122 \end{figure} | |
10 | 123 |
47 | 124 木構造の識別, つまり Map の キー にはString を利用する. |
125 String は Haskell の文字列の型で, Char のリスト [Char] の別名である. | |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
126 |
49 | 127 Jungle を作成するには, createJungle を用いる(ソースコード\ref{src:createJungle}). |
47 | 128 empty は空のMapを作成する関数である. |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
129 |
49 | 130 \begin{lstlisting}[label=src:createJungle, caption=createJungle] |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
131 createJungle :: IO Jungle |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
132 createJungle = atomically $ do |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
133 map <- newTVar empty |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
134 return (Jungle map) |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
135 \end{lstlisting} |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
136 |
49 | 137 |
138 createJungleは, 新たにSTMの変数を作成する newTVar を実行する. | |
139 newTVar などの STM の操作は STM モナド内で行う. | |
140 最後にatomicallyを行うことで, do 構文内がトランザクションとして実行される. | |
141 STMの関数が持つ型をソースコード\ref{src:stm}に示す. | |
142 | |
143 \begin{lstlisting}[label=src:stm, caption=STMの関数] | |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
144 newTVar :: a -> STM (TVar a) |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
145 readTVar :: TVar a -> STM a |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
146 writeTVar :: TVar a -> a -> STM () |
23 | 147 |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
148 atomically :: STM a -> IO a |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
149 \end{lstlisting} |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
150 |
60 | 151 ソースコード\ref{src:createJungle}の atomically の隣にある \$ は関数適用演算子である. |
47 | 152 \$ 関数は最も低い優先順位を持っており, 右結合である. |
49 | 153 括弧を減らすのに使う. \$ を使わない場合はソースコード\ref{src:dollar}の様に記述することになる. |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
154 |
49 | 155 \begin{lstlisting}[label=src:dollar, caption=関数適用演算子を使わない場合] |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
156 createJungle :: IO Jungle |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
157 createJungle = atomically (do |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
158 map <- newTVar empty |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
159 return (Jungle map)) |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
160 \end{lstlisting} |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
161 |
47 | 162 createJungle は, IOを返すため使う際には main に関連付ける必要がある. |
23 | 163 |
60 | 164 \clearpage |
165 \section{Tree} | |
47 | 166 Jungleが保持する木の情報は, 内部的には Tree というデータ型で保持している. |
49 | 167 Tree は木の名前と, ルートノードの情報を持っている(ソースコード\ref{src:tree}). |
47 | 168 実際にユーザがJungleを利用する際は, Jungle と木の名前を使ってルートノードを取ってくるため, Tree という構造は見えない. |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
169 |
47 | 170 ルートノードの情報はスレッド間で状態を共有する必要がある. |
171 スレッドセーフに取り扱う必要があるため, この情報も Haskell の ソフトウェア・トランザクショナル・メモリ (STM) を用いて管理している. | |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
172 |
49 | 173 \begin{lstlisting}[label=src:tree,caption=Treeのデータ型の定義] |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
174 data Tree = Tree |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
175 { rootNode :: (TVar Node) |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
176 , treeName :: String } |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
177 \end{lstlisting} |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
178 |
49 | 179 新たな非破壊的木構造を作るには, createTree を用いる(ソースコード\ref{src:createTree}). |
47 | 180 createTree は, createJungleで作成した Jungle と木の名前を String で受け取る. |
10 | 181 |
49 | 182 \begin{lstlisting}[label=src:createTree, caption=createTree] |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
183 createTree :: Jungle -> String -> IO () |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
184 createTree (Jungle tmap) tree_name = atomically $ do |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
185 map <- readTVar tmap |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
186 tree <- emptyTree tree_name |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
187 writeTVar tmap (insert tree_name tree map) |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
188 |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
189 emptyTree :: String -> STM Tree |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
190 emptyTree tree_name = do |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
191 node <- newTVar emptyNode |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
192 return (Tree node tree_name) |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
193 |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
194 emptyNode :: Node |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
195 emptyNode = Node (empty) (empty) |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
196 \end{lstlisting} |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
197 |
47 | 198 createJungleも STM を操作するため IOを返す. |
49 | 199 Jungle の持つ, 複数の木構造と名前を関連付けた Map をreadTVarで取得する. |
200 ルートノードの管理のための STM の変数をもった Tree を作成し, Jungle の Map に insert する. | |
201 そして最後に writeTVar を用いて STM を更新する. | |
47 | 202 writeTVar は更新する先の変数と, 更新内容の2つを受け取る STM の関数である. |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
203 |
49 | 204 実際にcreateJungleとcreateTreeを利用する時はソースコード\ref{src:createdatabase}のように記述する. |
34 | 205 |
49 | 206 \begin{lstlisting}[label=src:createdatabase,caption=データベースと木の作成] |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
207 main = do |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
208 jungle <- createJungle |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
209 createTree jungle "name of new tree here" |
10 | 210 \end{lstlisting} |
211 | |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
212 |
10 | 213 \subsubsection{ルートノード} |
47 | 214 非破壊的木構造データベース Jungle では, 木の最新の状態を更新・参照するのにルートノードを使う. |
215 ルートノードは, 最新の木構造の根がどれかの情報を保持している(図\ref{fig:getrootnode}). | |
23 | 216 |
217 \begin{figure}[!htbp] | |
218 \begin{center} | |
219 \includegraphics[scale=0.7]{./images/get_root_node.pdf} | |
220 \end{center} | |
221 \caption{ルートノード} | |
222 \label{fig:getrootnode} | |
223 \end{figure} | |
224 | |
47 | 225 ルートノードに関する関数を説明する. |
226 getRootNode は, 最新のルートノードを取得できる. | |
227 データベースと木の名前を渡すことで利用できる. | |
228 例えば, 図\ref{fig:getrootnode}の状態の時は, B というルートノードが取得できる. | |
10 | 229 |
49 | 230 |
60 | 231 getRootNode 関数の定義を示す(ソースコード\ref{src:getrootnode}). |
49 | 232 まず, readTVarでJungleが持つmapを参照する. |
233 Haskell の where キーワードは, 計算の中間結果に名前をつけるために用いられる. | |
234 今回は, root\_node という map を受け取る関数を定義している. | |
235 root\_node map では, Jungle が持つ Map をみて取得しようとしている名前の木構造があるかどうか調べている. | |
236 木構造があった場合, rootNodeというTreeに定義されているレコード構文のアクセサ関数を使って, (TVar Node)を取得する. | |
237 最後に, (TVar Node)に対して, readTVarを行うことで最新のルートノードが取得できる. | |
238 | |
239 \begin{lstlisting}[label=src:getrootnode, caption=最新のルートノードの取得] | |
34 | 240 getRootNode :: Jungle -> String -> IO Node |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
241 getRootNode (Jungle tmap) tree_name = atomically $ do |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
242 map <- readTVar tmap |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
243 readTVar (root_node map) |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
244 where |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
245 root_node map = case lookup tree_name map of |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
246 Just x -> rootNode x |
10 | 247 \end{lstlisting} |
248 | |
47 | 249 木構造を編集する関数は全て Node を受け取って Node を返す. |
250 その返ってきた Node をルートノードとして登録することで, 木構造の最新のルートノードが更新される. | |
251 updateRootNode は, データベースと木の名前, 変更して返ってきた木構造の 3 つを渡す. | |
252 updateRootNodeをした後は, getRootNodeで取得できるルートノードが更新された状態になっている. | |
10 | 253 |
60 | 254 updateRootNode 関数の定義を示す(ソースコード\ref{src:updaterootnode}). |
49 | 255 getRootNodeと同じように, Treeの(TVar Node)を取得し, 最後にwriteTVarを用いて更新している. |
256 | |
257 \begin{lstlisting}[label=src:updaterootnode, caption=ルートノードの更新] | |
34 | 258 updateRootNode :: Jungle -> String -> Node -> IO () |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
259 updateRootNode (Jungle tmap) tree_name node = |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
260 atomically $ do |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
261 map <- readTVar tmap |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
262 writeTVar (root_node map) node |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
263 where |
41 | 264 root_node map = case lookup tree_name map of |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
265 Just x -> rootNode x |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
266 \end{lstlisting} |
34 | 267 |
47 | 268 updateRootNodeWithは, ノードを更新する関数とデータベース, 木の名前を渡して利用する. |
269 ノードを更新する関数とは, ノードを受け取ってノードを返す関数である. (Node $->$ Node) がそれにあたる. | |
49 | 270 このupdateRootNodeWithを利用することで, getRootNodeをした後に編集しupdateRootNodeを行う一連の操作が分断されずに行われることが保証される. |
60 | 271 |
272 updateRootNodeWith 関数の定義を示す(ソースコード\ref{src:updaterootnodewith}). | |
49 | 273 updateRootNodeWithでは, 一連の操作を分断せずに行うためにreadTVarからwriteTVarまで同じ STM モナド内で行っている. |
274 atomicallyに関数にdo構文で繋げたSTMモナドを渡すことで, このブロックがトランザクションとして実行される. | |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
275 |
49 | 276 \begin{lstlisting}[label=src:updaterootnodewith, caption=ルートノードの更新] |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
277 updateRootNodeWith :: (Node -> Node) -> Jungle -> String -> IO () |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
278 updateRootNodeWith f (Jungle tmap) tree_name = |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
279 atomically $ do |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
280 map <- readTVar tmap |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
281 n <- readTVar (root_node map) |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
282 writeTVar (root_node map) (f n) |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
283 where |
41 | 284 root_node map = case lookup tree_name map of |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
285 Just x -> rootNode x |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
286 \end{lstlisting} |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
287 |
47 | 288 並列データベース Jungle で他のスレッドと状態を共有する操作は, |
289 createJungle, createTree, getRootNode, updateRootNode, updateRootNodeWith | |
290 で全てである. | |
291 並列データベース Jungle では, なるべく状態を共有しないようにすることで並列実行時の性能の向上を実現する. | |
49 | 292 ソフトウェアトランザクショナルメモリは書き込み時に他から変更があった場合にやり直しという操作はあるものの, 読み込みに関してはノンブロッキングで高速に読み込める. |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
293 |
60 | 294 \clearpage |
295 \section{Node} | |
47 | 296 Node は木構造を表現するデータ構造である. |
297 再帰的に定義されている. | |
298 各ノードは children として子ノードを複数持つことができる(図\ref{fig:node_components}). | |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
299 |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
300 \begin{figure}[!htbp] |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
301 \begin{center} |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
302 \includegraphics[width=110mm]{./images/node_component.pdf} |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
303 \end{center} |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
304 \caption{Nodeの構成要素} |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
305 \label{fig:node_components} |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
306 \end{figure} |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
307 |
47 | 308 children および attributes も Data.Map を用いて定義されている(ソースコード \ref{src:node}). |
40
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
309 |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
310 \begin{lstlisting}[label=src:node, caption=Nodeのデータ型の定義] |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
311 data Node = Node |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
312 { children :: (Map Int Node) |
bd30d93097da
describe Jungle and Tree
Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
parents:
35
diff
changeset
|
313 , attributes :: (Map String ByteString) } |
10 | 314 \end{lstlisting} |
315 | |
316 \subsubsection{木の編集} | |
47 | 317 木の編集には, Node を使う. |
318 木の編集に用いる関数は全て Node を受け取って Node を返す. | |
319 非破壊的木構造を利用しているため, getRootNode などで取得してきた Node は他のスレッドと干渉することなく自由に参照, 編集できる. | |
320 これらの編集のための関数は, 編集後updateRootNodeするか, ひとつの関数にまとめてupdateRootNodeWithをすることで木構造に反映させることができる. | |
10 | 321 |
49 | 322 編集対象のノードを指定するには, NodePath を利用する(図\ref{fig:nodepath}). |
47 | 323 NodePath は, ルートノードからスタートし, ノードの子どもの場所を次々に指定したものである. |
324 Haskell の基本データ構造であるリストを利用している. | |
10 | 325 |
326 \begin{figure}[!htbp] | |
327 \begin{center} | |
328 \includegraphics[width=100mm]{./images/nodepath.pdf} | |
329 \end{center} | |
330 \caption{NodePath} | |
331 \label{fig:nodepath} | |
332 \end{figure} | |
333 | |
47 | 334 木の編集を行う関数を紹介する. |
49 | 335 木の編集を行う関数の型の定義をソースコード\ref{src:editfunc_type}に示す. |
34 | 336 |
49 | 337 \begin{lstlisting}[label=src:editfunc_type, caption=木の編集を行う関数] |
41 | 338 addNewChildAt :: Node -> Path -> Node |
34 | 339 deleteChildAt :: Node -> Path -> Position -> Node |
41 | 340 putAttribute :: Node -> Path -> String -> ByteString -> Node |
341 deleteAttribute :: Node -> Path -> String -> Node | |
10 | 342 \end{lstlisting} |
343 | |
49 | 344 \paragraph*{addNewChildAt} |
345 ノードに新しい子を追加できる. | |
346 更新対象となる木構造の Node と, どこに追加するかの情報である NodePath を渡す必要がある. | |
347 子の場所は, インクリメントしながら自動的に指定される. | |
41 | 348 |
49 | 349 \paragraph*{deleteChildAt} |
350 ノードの子を削除できる. | |
351 更新対象となる木構造の Node と, どこのノードの子を削除するかという情報である NodePath, 削除したい子の場所を指定する Position を渡す必要がある. | |
41 | 352 |
49 | 353 \paragraph*{putAttribute} |
354 ノードに属性を追加できる. | |
355 更新対象となる木構造の Node と, どこに属性を追加するかの情報である NodePath を渡す必要がある. | |
356 属性はキーと値があり, キーは String, 値は ByteString である. | |
41 | 357 |
49 | 358 \paragraph*{deleteAttribute} |
359 ノードの属性を削除できる. | |
360 更新対象となる木構造の Node と, どこの属性を削除するかの情報である NodePath, 削除したい属性のキーである String を渡す必要がある. | |
41 | 361 |
47 | 362 これらの関数は, ほぼ同一の関数で定義できる. |
49 | 363 addNewChildAtを用いて説明する(ソースコード\ref{src:addNewChildAt}). |
23 | 364 |
60 | 365 \newpage |
49 | 366 \begin{lstlisting}[label=src:addNewChildAt, caption=木の編集を行う関数] |
41 | 367 addNewChildAt :: Node -> Path -> Node |
368 addNewChildAt parent [] = addChildAt parent emptyNode | |
369 addNewChildAt parent (x:xs) = addChild parent x $ addNewChildAt x_node xs | |
370 where | |
371 map = children parent | |
372 x_node = case lookup x map of | |
373 Just x -> x | |
34 | 374 |
41 | 375 addChild :: Node -> Position -> Node -> Node |
376 addChild node pos child = Node new_child attr | |
377 where | |
378 map = children node | |
379 new_child = insert pos child map | |
380 attr = attributes node | |
381 | |
382 addChildAt :: Node -> Node -> Node | |
383 addChildAt node child = Node new_child attr | |
384 where | |
385 map = children node | |
386 pos = (size map) + 1 | |
387 new_child = insert pos child map | |
388 attr = attributes node | |
10 | 389 \end{lstlisting} |
390 | |
47 | 391 非破壊的木構造の編集は再帰で定義できる. |
392 左結合となる\$を使い, 対象のノードに到達するまで, addChildを繰り返す. | |
49 | 393 addChildは, 指定したノードのPositionに子を追加する. 引数として子となるノードが必要である. |
394 addChildを繰り返すことで, 下の階層から徐々に上に作られていく. | |
23 | 395 |
47 | 396 addNewChildAt, deleteChildAt, putAttribute, deleteAttributeといった, |
397 非破壊的木構造の編集は, 対象のノードに対する操作以外は全て同じである. | |
398 Pathのリストが空になる, すなわち対象のノードに到達した時の操作だけが異なる. | |
399 新しい子を追加するのが addNewChildAt, 指定されたポジションの子を削除するのが deleteChildAt, | |
400 指定されたキーと値を追加するのが putAttribute, 指定されたキーの値を削除するのが deleteAttributeである. | |
10 | 401 |
49 | 402 \subsubsection{木の参照} |
403 木の参照にも参照対象となる木構造の Node を用いる. | |
404 参照関数の定義をソースコード\ref{src:reffunc}に示す. | |
10 | 405 |
49 | 406 \begin{lstlisting}[label=src:reffunc, caption=参照関数] |
407 getNode :: Node -> Path -> Node | |
408 getNode node [] = node | |
409 getNode node (x:xs) = getNode child xs | |
410 where | |
411 map = children node | |
412 child = case M.lookup x map of | |
413 Just x -> x | |
34 | 414 getAttributes :: Node -> Path -> String -> Maybe ByteString |
41 | 415 getAttributes node path key = lookup key map |
416 where | |
417 target = getNode node path | |
418 map = attributes target | |
34 | 419 |
41 | 420 getChildren :: Node -> Path -> [Node] |
421 getChildren node path = elems map | |
422 where | |
423 target = getNode node path | |
424 map = children target | |
425 | |
426 assocsChildren :: Node -> Path -> [(Int, Node)] | |
427 assocsChildren node path = assocs map | |
428 where | |
429 target = getNode node path | |
430 map = children target | |
2 | 431 |
41 | 432 assocs :: Node -> Path -> [(String, ByteString)] |
433 assocs node path = assocs map | |
434 where | |
435 target = getNode node path | |
436 map = attributes target | |
23 | 437 |
41 | 438 numOfChild :: Node -> Path -> Int |
439 numOfChild node path = size map | |
440 where | |
441 target = getNode node path | |
442 map = children target | |
34 | 443 |
41 | 444 currentChild :: Node -> Path -> Maybe Node |
445 currentChild node path = lookup pos map | |
446 where | |
447 target = getNode node path | |
448 map = children target | |
449 pos = size map | |
10 | 450 \end{lstlisting} |
451 | |
60 | 452 参照関数の基本的な流れは, getNode関数を使って参照したいPathのノードを取ってくることである. |
453 そのノードにはwhereキーワードを利用して, targetという名前をつけている. | |
454 targetに対して, 子のMapや属性のMapを取得した後, lookup関数などを適用する. | |
455 elems, assocs, sizeなどはData.Mapの参照関数で, Jungle ではその関数をそのまま利用している. | |
23 | 456 |
49 | 457 参照関数の基本的な機能をまとめて説明する. |
34 | 458 |
49 | 459 \paragraph*{getAttributes} |
460 対象の Path に存在する属性を Key を用いて参照できる. | |
461 | |
462 \paragraph*{getChildren} | |
463 対象の Node が持つ全ての子を Node のリストとして返す. | |
47 | 464 あるNodeに存在する全ての子に対して, 参照を行いたい場合に利用する. |
23 | 465 |
49 | 466 \paragraph*{assocsChildren} |
467 対象の Node が持つ全ての子を Position とのタプルにし, そのタプルのリストを返す. | |
47 | 468 あるNodeに存在する全ての子に対して, 子のPositionを取得しながら参照を行いたい場合に利用する. |
34 | 469 |
49 | 470 \paragraph*{assocsAttribute} |
471 対象の Node が持つ全ての属性を, キーと値のペアとし, そのペアのリストを返す. | |
47 | 472 あるNodeに存在する全ての属性に対して, 参照を行いたい場合に利用する. |
10 | 473 |
49 | 474 \paragraph*{numOfChild} |
475 対象の Node が持つ子どもの数を取得できる. | |
10 | 476 |
49 | 477 \paragraph*{currentChild} |
478 対象の Node が持つ最新の子を取得できる. | |
23 | 479 |