Reputation: 17707
EDIT: To be clear. This is a question about how to do something with meta programming. It's not about memoizing. Clearly there are better ways to memoize. The relevant methods have "memoize" in them just to illustrate their purpose.
I'm just toying around with meta programming, so please don't answer use a
@foo
instance variable.
I have the following that tries to memoize both an instance and a class method by overwriting the method definition from the running method..
class Obj
class << self
def meta_me; self; end
def class_memoize
puts "hard core calculating ..."
abc = "huge calculation result"
raise "broken here with infinite loop"
define_class_method "class_memoize" do
puts abc
abc
end
class_memoize
end
def define_class_method name, &blk
meta_me.instance_eval do
define_method name, &blk
end
end
end
def instance_memoize
puts "hard core calculating ..."
abc = "huge calculation result"
self.class.meta_me.send :define_method, :instance_memoize do
puts abc
abc
end
instance_memoize
end
end
o = Obj.new
o.instance_memoize
# hard core calculating ...
# huge calculation result
o.instance_memoize
# huge calculation result
The instance version works, but the class version does not.
I've left in an attempt at the class version for reference.
Upvotes: 1
Views: 809
Reputation: 3693
Ethics aside, It's much easier than you think. Your main issue is you're using the wrong thing for your meta_me method. Try this:
class Object
def metaclass
class<<self;self;end
end
end
This is a fairly common monkeypatch when doing metaprogramming in Ruby. Now it's easy to reimplement a method, dynamically:
class Obj
def self.class_memoize
puts "calculating..."
abc = "result"
metaclass.send(:define_method, :class_memoize) do
puts abc
abc
end
class_memoize
end
def instance_memoize
puts "calculating..."
abc = "result"
metaclass.send(:define_method, :instance_memoize) do
puts abc
abc
end
instance_memoize
end
end
As you can see, once you have the metaclass object, redefining the methods done the same way whether it's a class method or an instance method. Note that if you don't want to sully the whole namespace with a metaclass method, it's probably better just to use the class<<self;self;end
idiom whenever you want to refer to it. You can call methods directly on it, like this:
(class<<self;self;end).send(:define_method, :foo){|bar| bar*23}
Note that the parens aren't truly needed, it just helps to contain the messiness that is the bare metaclass reference :) Hope this helps.
Upvotes: 1