我在ruby中的对象中重新定义一个方法,我需要将new方法作为闭包。例如:
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
任何想法怎么做?
感谢。
答案 0 :(得分:23)
这是有效的(在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 #=> #<Class:#<String:0x3c788a0>>
# ... nope! singleton_class is *not* String
# Keep reading if you're curious :)
这是有效的,因为你正在打开str singleton class并在那里定义一个方法。因为这个,以及对Module#define_method的调用,有些人称之为“平坦范围”,如果你使用def to_s; 'whatever'; end
,你就能够访问超出范围的变量。
你可能想在这里查看其他一些“元编程法术”:
media.pragprog.com/titles/ppmetr/spells.pdf
为什么它只会更改str
?
因为Ruby有几个有趣的技巧。在Ruby对象模型中,方法调用导致接收者不仅搜索它的类(和它的祖先),而且还搜索它的单例类(或者像Matz所称的那样,它是本征类)。这个单例类允许您[重新]定义单个对象的方法。这些方法称为“单例方法”。在上面的示例中,我们正在这样做 - 定义单例方法名称to_s
。它的功能与此完全相同:
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!
而且,如果你很好奇......
你知道课程方法吗?或者,在某些语言中,我们称之为静态方法?好吧,那些确实存在于Ruby中。在Ruby中,类方法实际上只是在Class对象的单例类中定义的方法。
如果这听起来很疯狂,请查看我上面提供的链接。如果你知道如何进行元编程,那么很多Ruby的功能都只能被挖掘 - 在这种情况下你真的想知道单例类/方法,更一般地说,知道Ruby对象模型。
HTH
- 查尔斯
答案 1 :(得分:9)
Feature #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
所涉及的概念仍然与我的previous answer中的概念相同,后者提供了有关如何在Ruby的对象模型中工作的更深入的描述,以及Object#define_method
定义这在概念上与Ruby 1.9.2&#39; Object#define_singleton_method
相同。
您可能会发现对类似任务有用的其他方法:
答案 2 :(得分:0)
这似乎有效。
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
不起作用,因为它是一个类方法,这意味着您必须在对象的类上调用它,将该代码提供给该类的所有实例,而不仅仅是此实例。 / p>