Help us understand the problem. What is going on with this article?

[Ruby]Hashのdefault_procで簡易キャッシュ

More than 5 years have passed since last update.

プログラム中で、ちょっとしたキャッシュを作りたい場合。もちろんメソッドを用意してもいいのですが、再利用性が低かったり、ちょっと大袈裟感があったりする場合も。

def foo
  %(a b c b a d).each do |id|
    puts sample_item(id)
  end
end
def sample_item id
  @sample_cache[id] ||= Sample.find(id)
end

そんな時には、Hashのdefault_procを使うと簡易的に代替できます。

def foo
  sample_cache = {}
  sample_cache.default_proc = proc do |hash, key|
    hash[key] = Sample.find(key)
  end
  %(a b c b a d).each do |id|
    puts sample_cache[id]
  end
end

コンストラクタにブロックを渡すのでもOK。

def foo
  sample_cache = Hash.new{|hash, key|
    hash[key] = Sample.find(key)
  }
  %(a b c b a d).each do |id|
    puts sample_cache[id]
  end
end

一応、レンスポンス的にもこちらの方が速い。

benchmark
require 'benchmark'

class TestClass
  def initialize keys, cycles
    @keys = keys
    @cycles = cycles
    @cache = {}
  end
  def get_data i
    @cache[i] ||= Time.now
  end
  def test1
    ret = {}
    (1..@keys).cycle(@cycles){|i| ret[i] = get_data(i)}
  end
  def test2
    ret = {}
    cache = Hash.new{|h,k| h[k]=Time.now}
    (1..@keys).cycle(@cycles){|i| ret[i] = cache[i]}
  end
end

t = TestClass.new(10000, 1000)
Benchmark.bm do |x|
  x.report { t.test1 }
  x.report { t.test2 }
  x.report { t.test2 }
  x.report { t.test1 }
end
result
       user     system      total        real
   2.860000   0.010000   2.870000 (  2.866910)
   2.150000   0.000000   2.150000 (  2.162081)
   2.180000   0.000000   2.180000 (  2.188632)
   2.980000   0.000000   2.980000 (  2.983547)
nao58
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away