Thursday, October 20, 2005

Ruby Gem

from here:

problem:

I have a class Foo which has a number of heavy, CPU intensive, number
crunching methods that take parameters.

class Foo
attr_reader :x, :y, ...

def calc1(a,b,c)
...# complicated, time-consuming calculation...
end
def calc2(a,b)
...# complicated, time-consuming calculation...
end
...
end

Foo is immutable. If a client calls a method more than once using the
same set of parameters, the same result will be calculated and
returned.

To avoid the work of recalculating a result if a client calls a method
more than once with the same set of parameters, I revised Foo to cache
results in a hash for each method for each set of parameters. The
keys in the hashes are arrays of parameters.

class Foo
def calc1(a,b,c)
param_array = [a,b,c]
@cache_calc1 ||= {}
return @cache_calc1[param_array] if @cache_calc1[param_array]

ans = ... #same complicated, time-consuming calculation...
@cache_calc1[param_array] = ans
end

def calc2(a,b)
param_array = [a,b]
@cache_calc2 ||= {}
return @cache_calc2[param_array] if @cache_calc2[param_array]

ans = ... #same complicated, time-consuming calculation...
@cache_calc2[param_array] = ans
end
end

This works great -- calculations for a given set of parameters on
each method are now done only once. However, the code seems rote,
repetitive and intrusive to the original methods.

Is there a way to do this in Ruby in a more declarative, more DRY way?

Solution:

> You could check out Daniel Berger's memoize (based on Nobu Nokada's
> original I think) on RAA (http://raa.ruby-lang.org/project/memoize).

Thank you and wowser! memoize seems to do just what I'd been looking
to do. A handful of lines. I can see it works.

#my test code
f = Foo.new
f.memoize(:calc1)
f.memoize(:calc2)
f.calc1(1,2,3)
f.calc1(1,2,3) #yep its working
f.calc1(7,8,9) #yep its working

If I can ask a Ruby 101 question about it... Isn't "cache" in the
module (pasted below in its entirety it is so short) only a local
variable? Why does is retain state? How can one, for example, access
the cache to see the state of the cache?

Thanks!

--Brian

module Memoize
MEMOIZE_VERSION = "1.0.0"
def memoize(name)
meth = method(name)
cache = {}
(class << self; self; end).class_eval do
define_method(name) do |*args|
cache[args] ||= meth.call(*args)
end
end
end
end



Delicious.

No comments: