Concurrent Clean : Finger trees : listToTree
最近、こればっか。
リストを与えられて、そこからFinger treeを作るという関数 listToTree を作るとして、どうやって作るか。
単純には、pushLを連続的に適用して、
listToTree ls = foldr pushL Empty ls
とやって、pushLがO(1)なのでこれでもよいのだ(し、論文はこれで書いている)けれど、もっと効率よくできるはずだよね、という。
例えば、
listToTree ls # len = length ls pr = take 4 ls sf = drop (len - 4) ls md = take (len - 8) (drop 4 ls) = Deep pr (listToTree (nodes md)) sf
というのはどうか、とか思うわけだけれど、これには罠が。再帰するたびにlengthを呼んでいるのが微妙だ。もっとも、nodesがリストの長さを1/3にするので、線形であることに変わりはないのだけれど、嬉しくない。
じゃあ、こういうのはどうだろう?
listToTree ls # len = length ls (p,s) = splitAt (len / 2) ls = toTree p (reverse s) toTree [] [] = Empty toTree [a] [] = Single a toTree [] [a] = Single a toTree p s # (pr,pm) = splitAt 4 p (sf,sm) = splitAt 4 s pm = nodes pm sm = reverse_nodes sm = Deep pr (toTree pm sm) (reverse sf)
これならlengthを一回しか呼ばないし、リストの先頭にしかアクセスしないので、効率がいいんじゃないかな?