Function.prototype.cached

この前の続き。

Function.prototype.cached = function() {
  var f = this;
  var args = arguments;
  f._cache = new Cache(function_name(f) || "",
                       args[0] || 10,
                       args[1] || function(){return 1},
                       args[2] || function(){return 1});
  return function() {
    var cache = f._cache.get(arguments);
    if (cache) {
      return cache;
    }
    else {
      cache = f.apply(f, arguments);
      f._cache.set(arguments, cache);
      return cache;
    }
  };
};

// via http://d.hatena.ne.jp/brazil/20050925/1127591042
function function_name(f){
  var s = ("" + f).match(/function (\w*)/)[1];
  return isEmpty(s) ? "[anonymous]" : s;
}

search関数が{search_word: xxx, page_no: yyy}を引数にもらうものとすると、

var cached_search = search.cached(
  2,
  function(o) {return reduce(operator.logand,
                             map(function(k) {return o[0].hasOwnProperty(k)},
                                 ['search_word', 'page_no']))},

  function(o) {return '_' + map(function(k) {
                                return o[0][k]}, ['search_word', 'page_no']).join('-')}
);

とすることでcached_search経由の関数コールは最近の2つ分だけキャッシュされるけど、キャッシュしたい関数毎にこれだけ長い関数書くのは面倒なので、

Function.prototype.cached_by_arguments = function(cache_size, arguments_length) {
  var f = this;
  var key_checker, key_generator;
  if (arguments_length > 1) {
    key_checker = function(o) {
      return (o.length == arguments_length);
    };
    key_generator = function(o) {
      var args = extend([], o);  // bk?
      return '_' + args.join('-');
    };
  }
  else {
    key_checker = function() {
      return 1;
    };
    key_generator = function() {
      return '_' + arguments[0][0];
    };
  }
  return f.cached(cache_size || 10,
                  key_checker,
                  key_generator);
};

も書いた。これを使うことで引数が配列やハッシュでなく文字列や数値な関数であれば(最初の例と用途違うけど。。)、

function search(search_word, page_no) {
  //...
}
var cached_search = search.cached_by_arguments(2, 2);

とだいぶ短く書ける。
信頼のおけるブロガーさんのPerlでのキャッシュモジュールの記事にインスパイアされました。あとplaggerPlagger::Cache::get_callbackとか。