Converting method to cached method
So, we have a remarkably clear and simple realization of compose, curry and memoize methods for
objects pretending to be a method (Proc, Method, any others having :arity method and
including mixing Functional) (see
FunctionalProgrammingOnRuby and
original post of David Flanagan).
Method :memoize makes proc caching it's values from any method/proc/..
But what we really want to have is method :memoize!, that returns result of receiver's class
and make all calls to the method calls to it's cached version.
module Functional
def memoize
cache = {}
lambda {|*args|
# calculate value if not cached and cache it
# remark: nils will be recalculated each time.
cache[args] ||= self[*args]
}
end
end
class Method; include Functional; end
class Proc; include Functional; end
def fib(n)
if n == 1 || n == 0
1
else
fib(n-1) + fib(n-2)
end
end
fib(30) # slow
a = method(:fib)
a.memoize.call(30) #still slow, but why?
#
# we need to change all fib-calls to cached version of :fib
# Let's see what we can do.
alias :old_fib :fib
def fib(n)
@cache ||= {}
@cache[n] ||= old_fib(n)
end
puts fib(100) # It's fast now!
# Let's write general purpose function
def make_cached_method(name)
not_cached_name = (name.to_s + '_not_cached')
# eval is not good but works
eval "
alias :#{not_cached_name} :#{name}
def #{name}(*args)
@cache ||= {}
@cache[args] ||= #{not_cached_name}(*args)
end
"
end
make_cached_method(:fib)
puts fib(100) # It's fast too
# Now we need memoize! for Method
# Can you write the _right_ version of memoize! ?
# (notice, that method :replace is not available for Proc and Method objects)
See
Advanced caching of method results
--
ArtemVoroztsov - 18 Oct 2007