トップ «前の日記(2008-07-31) 最新 次の日記(2008-08-08)» 編集

Route 477



2008-08-07

[ruby] RubyでZipperを実装してみた

Zipperについては

を見てほしいのですが、簡単に言うと副作用を使わずに実装された双方向リストみたいな感じです。

例。

# zipperを作る
zipper = Zipper.make(1, 2, 3)
# 最初は、0番目にカーソルがある
zipper.get   #=> 1
# nextを呼ぶと、1番目にカーソルが移動する
zipper.next.get  #=> 2

んで書き換えもできるのですが、「副作用なし」なので、書き換えると丸ごと別のzipperができます。

orig = Zipper.make(1, 2, 3)
copy1 = orig.next.set(100)
copy2 = orig.next.set(200)

copy1.to_a   #=> [1, 100, 3]
copy2.to_a   #=> [1, 200, 3]

「なんだか遅そう」って?それがO(1)、つまりどんなに長くなっても同じ時間で行えるのがZipperの凄いところなんです。

試しにこんなテストを書いてみました。

 it "Zipperは、大量にコピーしてそれぞれ書き換えたりしてもへっちゃら!" do
   original = Zipper.make(*(0..10000).to_a)   # 長さ10000のzipperを作って、
   zippers = (0..10000).map{|i|               # それを10000個、
     original.next.set(i)                     # 1箇所ずつ書き換えて複製する
   }
   zippers[99].get.should == 99
   zippers[1234].get.should == 1234
 end

二次元配列でこれをやると10000 * 10000なので凄い時間がかかりそうですが、Zipperでは0.5秒で終わります(僕のMacBook調べ)。

まあ、Rubyのライブラリとして何に使えるかというと、ぱっと思いつかないのですが(笑)、なかなか面白かったです。

ソースは最近話題の(って遅いけど) gist にアップロードしてみました。間違ってゲストで投稿しちゃったんだけど、その場でログインしたらownerがyharaになるとか気が利いてますね。