我想在Ruby对象中添加一个带闭包的单例方法

时间:2009-07-17 00:29:07

标签: ruby methods singleton dynamic-programming

我希望为特定对象添加单例方法。我希望当一个对象的实例方法首次被调用时,它会做一些工作,然后为所述同名对象(包含工作)创建一个单例方法。在对所述对象的所有后续调用中,单例方法将遮蔽实例方法并将被调用。

我知道如何创建单例方法,我的问题是我希望创建的单例方法调用lambda(在这种情况下为l)。 def不会创建闭包,所以我不能在随后调用该方法时引用变量l(下面的代码)(在这个例子中注释掉了l.call())我想知道在创建单例时如何创建闭包特定对象的方法。任何帮助,将不胜感激。谢谢。

class Thing
end

t = Thing.new
t2 = Thing.new

Thing.instance_eval() do
  def speak
    puts "I speak for all Things, I am a class method"
  end
end

Thing.class_eval() do
  def speak
    puts "This is the instance method referenced by the Thing object #{self}"
    r = "something I wish to hold on to, maybe expensive to calculate"
    l = lambda {puts r}
    instance_eval() do
      def speak()
        puts "This is the singleton method in the Thing object #{self}"
        #l.call() # I want this to work! How?
      end
    end
  end
end

Thing.speak()
t.speak()
t2.speak()
t.speak()
t2.speak()

运行时给出以下结果:(我将'<'更改为'#',因此它们以html显示)

  

我代言所有事情,我是一个班级   方法

     

这是引用的实例方法   由Thing对象#Thing:0x1d204>

     

这是引用的实例方法   由Thing对象#Thing:0x1d1dc>

     

这是单身人士的方法   事物对象#Thing:0x1d204>

     

这是单身人士的方法   事物对象#Thing:0x1d1dc>

3 个答案:

答案 0 :(得分:2)

您可以使用define_method定义带有块的方法。

示例:

class Object
  def eigenclass
    class <<self; self end
  end
end

a = "Hello"
other_word = "World"
a.eigenclass.class_eval do
  define_method(:cliche) {"#{self} #{other_word}"}
end
a.cliche # => "Hello World"
"Goodbye".cliche # => NoMethodError: undefined method `cliche' for "Goodbye":String

以下是define_singleton_method方法的实现:

class Object
  def define_singleton_method(name, &block)
    eigenclass = class<<self; self end
    eigenclass.class_eval {define_method name, block}
  end
end

答案 1 :(得分:2)

现在1.9已经用完了,你可以使用define_singleton_method:

jruby --1.9 -S irb
irb(main):019:0> fn = -> { length * 10 }
=> #<Proc:0x77cb8e0f@(irb):19 (lambda)>
irb(main):020:0> s.define_singleton_method :length_times_ten, fn
=> #<Proc:0x77cb8e0f@(irb):19 (lambda)>
irb(main):021:0> s
=> "a string"
irb(main):022:0> s.length_times_ten
=> 80

答案 2 :(得分:1)

嗯,一种方法是将它打包成一个实例变量:

(仅供参考,您可以class Thing重新开启Thing(它比使用#class_eval短一点,而且您不需要#instance_eval从内部定义方法方法)。

class Thing
  def speak
    puts "This is the instance method referenced by the Thing object #{self}"
    r = "something I wish to hold on to, maybe expensive to calculate"
    @l = lambda {puts r}
    instance_eval do 
      def speak()
        puts "This is the singleton method in the Thing object #{self}"
        @l[]
      end
    end
  end
end

这将重新定义#speak,但仅限于Thing的该实例。 Thing的其他实例仍将具有原始定义。

正如Chuck所指出的,另一种选择是使用与实例关联的单例类(也称为元类,也称为本征类)。单例类是存储与对象关联的所有单例方法的对象。您可以使用有趣的class <<object ; ... ; end语法(类似于普通类的#class_eval给出的上下文)来获取单例类评估的上下文。

class Thing
  def speak
    puts "This is the instance method referenced by the Thing object #{self}"
    r = "something I wish to hold on to, maybe expensive to calculate"
    singleton_class = class <<self # open singleton class context for current instance
      # in this context, self now refers to the singleton class itself
      self
    end
    l = lambda {puts r}
    singleton_class.class_eval do
      # since we used #class_eval, local variables are still in scope
      define_method(:speak) do 
        puts "This is the singleton method in the Thing object #{self}"
        # since we used #define_method, local variables are still in scope
        l[]
      end
    end
  end
end