1

编辑:要清楚。这是一个关于如何使用元编程做某事的问题。这不是关于记忆。显然有更好的方法来记忆。相关方法中有“memoize”只是为了说明它们的目的。


我只是在玩元编程,所以请不要回答使用 @foo实例变量。

我有以下尝试通过覆盖运行方法中的方法定义来记忆实例和类方法。

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

实例版本有效,但类版本无效。

我已经尝试在类版本中进行参考。

4

1 回答 1

1

除了道德,这比你想象的要容易得多。您的主要问题是您的 meta_me 方法使用了错误的东西。试试这个:

class Object
  def metaclass
    class<<self;self;end
  end
end

在 Ruby 中进行元编程时,这是一个相当常见的猴子补丁。现在很容易动态地重新实现一个方法:

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

如您所见,一旦您有了元类对象,无论是类方法还是实例方法,重新定义方法都一样。请注意,如果您不想使用元类方法玷污整个命名空间,那么class<<self;self;end在您想要引用它时使用该惯用语可能会更好。您可以直接在其上调用方法,如下所示:

(class<<self;self;end).send(:define_method, :foo){|bar| bar*23}

请注意,括号并不是真正需要的,它只是有助于包含作为裸元类引用的混乱 :) 希望这会有所帮助。

于 2012-11-30T12:18:15.937 回答