定义一个Ruby中的闭包方法

我正在ruby中的对象中重新定义一个方法,我需要将新方法作为闭包。 例如:

def mess_it_up(o) x = "blah blah" def o.to_s puts x # Wrong! x doesn't exists here, a method is not a closure end end 

现在如果我定义一个Proc,它就是一个闭包:

 def mess_it_up(o) x = "blah blah" xp = Proc.new {|| puts x # This works end # but how do I set it to o.to_s. def o.to_s xp.call # same problem as before end end 

有什么想法怎么做?

谢谢。

这工作(在irb中测试):

注意:这仅更改str – 而不是所有String实例。 请阅读下面的详细信息,了解其工作原理

 another_str = "please don't change me!" str = "ha, try to change my to_s! hahaha!" proc = Proc.new { "take that, Mr. str!" } singleton_class = class << str; self; end singleton_class.send(:define_method, :to_s) do proc.call end puts str.to_s #=> "take that, Mr. str!" puts another_str.to_s #=> "please don't change me!" # What! We called String#define_method, right? puts String #=> String puts singleton_class #=> #> # ... nope! singleton_class is *not* String # Keep reading if you're curious :) 

这是有效的,因为你打开str的单例类并在那里定义一个方法。 因为这个,以及对Module#define_method的调用,有些人称之为“平坦范围”,如果使用def to_s; 'whatever'; end ,你可以访问超出范围的变量def to_s; 'whatever'; end def to_s; 'whatever'; end def to_s; 'whatever'; end

您可能想在这里查看其他一些“元编程法术”:

media.pragprog.com/titles/ppmetr/spells.pdf

为什么它只改变str

因为Ruby有几个有趣的技巧。 在Ruby对象模型中,方法调用导致接收者不仅搜索它的类(和它的祖先),而且还搜索它的单例类(或者像Matz所称的那样,它是本征类)。 这个单例类允许您[重新]定义单个对象的方法。 这些方法称为“单例方法”。 在上面的示例中,我们正在这样做 – 定义单例方法名称to_s 。 它的function与此相同:

 def str.to_s ... end 

唯一的区别是我们在调用Module#define_method时使用闭包,而def是一个关键字,这会导致范围的变化。

为什么它不能更简单?

嗯,好消息是你用Ruby编程,所以随意发疯:

 class Object def define_method(name, &block) singleton = class << self; self; end singleton.send(:define_method, name) { |*args| block.call(*args) } end end str = 'test' str.define_method(:to_s) { "hello" } str.define_method(:bark) { "woof!" } str.define_method(:yell) { "AAAH!" } puts str.to_s #=> hello puts str.bark #=> woof! puts str.yell #=> AAAH! 

而且,如果你很好奇……

你知道class级方法吗? 或者,在某些语言中,我们称之为静态方法? 好吧,那些在Ruby中并不存在。 在Ruby中,类方法实际上只是在Class对象的singleton类中定义的方法。

如果这听起来很疯狂,请看看我上面提供的链接。 如果你知道如何进行元编程,那么很多Ruby的function都只能被挖掘 – 在这种情况下你真的想知道单例类/方法,更一般地说,是Ruby对象模型。

HTH

– 查尔斯

在Ruby 1.9.2中实现的function#1082使用Object#define_singleton_method使这变得简单:

 def mess_it_up(o) x = "blah blah" # Use Object#define_singleton_method to redefine `to_s' o.define_singleton_method(:to_s) { x } end 

所涉及的概念仍然与我之前的答案相同,后者提供了对Ruby如何在Ruby的对象模型中工作的更深入的描述,以及在概念上与Ruby 1.9.2的Object#define_singleton_method相同的Object#define_method定义。 Object#define_singleton_method

您可能会发现对类似任务有用的其他方法:

  • 对象#singleton_class
  • 对象#singleton_methods
  • 对象#respond_to_missing? (很棒的博客文章)

这似乎有效。

 class Foo def mess_it_up(o) x = "blah blah" o.instance_variable_set :@to_s_proc, Proc.new { puts x } def o.to_s @to_s_proc.call end end end var = Object.new Foo.new.mess_it_up(var) var.to_s 

问题是def中的代码在运行之前和新范围内都不会被评估。 因此,您必须先将块保存到对象上的实例变量中,然后再将其解除。

并且define_method不起作用,因为它是一个类方法,这意味着你必须在对象的类上调用它,将该代码提供给该类的所有实例,而不仅仅是这个实例。