Раздел «Язык Ruby».MethodToCachedMethod:

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