Module#prepend was added in Ruby 2+ specifically in because it can (among other things) act as a method combinator/decorator similar to CLOS or Python. In that way, you don't actually need to get access to the method itself, you can just override it.
class Module def memoize(meth) prepend(Module.new do memo = {} define_method(meth) do |*args, &blk| memo[[self, *args, blk]] ||= super(*args, &blk) end end) end end class Integer memoize def fib raise ArgumentError if self < 0 return self if self < 2 pred.fib + pred.pred.fib end end require 'benchmark' puts Benchmark.measure { p 42.fib }
In older versions of Ruby (1.9 or older), you would have to do something like this:
class Module def memoize(meth) memo = {} old_meth = instance_method(meth) define_method(meth) do |*args, &blk| memo[[self, *args, blk]] ||= old_meth.bind(self).(*args, &blk) end end end
Also, def evaluating to a Symbol denoting the name of the method being defined was added in Ruby 2.2, so, in older versions, you have to do this instead:
class Integer def fib raise ArgumentError if self < 0 return self if self < 2 pred.fib + pred.pred.fib end memoize :fib end
We could use a trick such as the one Rake uses for its desc method, though, to make it memoize the next method being defined:
class Module def memoize(meth=nil) return @__memoize_next_method__ = true unless meth memo = {} old_meth = instance_method(meth) define_method(meth) do |*args, &blk| memo[[self, *args, blk]] ||= old_meth.bind(self).(*args, &blk) end end def method_added(meth) return if @__recursing__ @__recursing__ = true # protect against infinite recursion if @__memoize_next_method__ memoize(meth) @__memoize_next_method__ = nil end @recursing = nil end end class Integer memoize def fib raise ArgumentError if self < 0 return self if self < 2 pred.fib + pred.pred.fib end end
@instance_variableput_fn = Kernel.method(:puts).to_proc.