Javascript上のシンプルで汎用的なキャッシュ

Javascriptで超簡単なLRUキャッシュを書いて使ってみたら凄く便利なのでシェア。

ソースコード

Chiral's Gistにあります。Javascript処理系組み込みのargumentsオブジェクトが配列でないのでコピーするところが特別ですが、あとはユーザが指定したサイズの配列をリニアサーチしてヒットしたらLRUで並べ替え、無かったらapply関数を読んでエントリを先頭に追加、というシンプルな実装です。

使い方

var cache = require('./cache.js');

var cached = cache.LRU(100); // 100 LRU entries.

// "some_func(arg1,arg2)" を以下のように置き換えて使う
var result = cached(some_func,arg1,arg2); 

LRU関数にサイズを与えると初期化されたキャッシュを包んだクロージャが返ってくるので、キャッシュを入れたい関数呼び出しのコードをそのクロージャを使って呼び出すだけです。===演算子を使って(引数がオブジェクトの場合はオブジェクト参照で一致、値の場合は値として一致)全ての引数が一致しているキャッシュエントリを探します。(キャッシュヒットした場合は元の関数を呼ばないので副作用があるようなものは注意。)

全く異なる関数を一つのキャッシュに混在させることも出来ます。

var r1=cached(some_func,arg1,arg2) // some_func(arg1,arg2)
var r2=cached(other_func,arg2) // other_func(arg1)

LRUアルゴリズムを使って、初期化時に与えたサイズの中でやりくりします。

キャッシュを複数作って関数ごとに分けてももちろんOK

var cached1 = cache.LRU(10); // compact size
var cached2 = cache.LRU(100); // midium size 

var r1=cached1(some_func,arg1,arg2) // some_func(arg1,arg2)
var r2=cached2(other_func,arg2) // other_func(arg1)

パフォーマンスのためthisオブジェクトをケアしてない(nullを設定する)ので関数内でthisを参照したい場合はこちらを使って

var r=cached(this.func,this,arg1,arg2,arg3) // this.func(arg1,arg2,arg3)

という風にすると良いかもしれない。

npmパッケージにしたら便利かなと思ったが、余りにも短すぎるコードなので逡巡。配布チャネルとしてNPMを使うんだということにして作ってみようか。

Javascriptはこういう「子供心にブロック玩具で遊んでるみたいな感じ」があるので凄く好きな言語です。