定义一个在Ruby中是闭包的方法 [英] Define a method that is a closure in Ruby
问题描述
我在ruby中的一个对象中重新定义一个方法,我需要一个新的方法是一个闭包。例如:
def mess_it_up(o)
x =blah blah
def o.to_s
puts x#错误! x不存在这里,一个方法不是一个闭包
end
end
现在如果我定义一个Proc,它是一个闭包:
def mess_it_up(o)
$ p $
x =blah blah
xp = Proc.new {||
puts x#这工作
end
#但是如何设置为o.to_s。
def o.to_s
xp.call#和以前一样的问题
end
end
任何想法如何做?
谢谢。
解决方案这样工作(在irb测试):
注意:这只改变
str
- 不是所有的String实例。请阅读下文,了解为什么会有效。another_str =请不要改变我!
str =ha,尝试改变我的to_s!hahaha!
proc = Proc.new {take that,Mr. str! }
singleton_class = class<< str;自; end
singleton_class.send(:define_method,:to_s)do
proc.call
end
puts str.to_s#=> 拿那个,先生!
puts another_str.to_s#=> 请不要改我!
#什么!我们调用String#define_method,对吧?
puts String#=> String
puts singleton_class#=> #< Class:#< String:0x3c788a0>
#... nope! singleton_class is * not * String
#如果你好奇,继续阅读:)
因为您正在打开str的单身课,并在其中定义方法。因为这一点,以及对 Module#define_method 的调用,有些人称为平面范围,你可以访问超出范围的变量,如果你使用
def to_s; '随你';结束
。
您可能想在这里查看其他一些元编程法术:
media.pragprog.com/titles/ppmetr/spells.pdf
为什么只更改str
strong>
因为Ruby有一些有趣的技巧,它的袖子。在Ruby对象模型中,方法调用导致接收者不仅搜索它的类(以及它的祖先),而且搜索单例类(或者Matz称之为eigenclass)。这个单例类是什么允许你为单个对象定义一个方法。这些方法称为单例方法。在上面的例子中,我们做的只是 - 定义一个单例方法名称
to_s
。它的功能与此相同:def str.to_s
...
end
唯一的区别是,当调用
Module#define_method
,而def
是一个关键字,导致范围的更改。
为什么不能更简单?
好吧,好消息是,你使用Ruby编程,所以你可以疯狂: p>
class Object
def define_method(name,& block)
singleton = class<自;自; 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#=>纬!
puts str.yell#=> AAAH!
如果你好奇...
你知道类方法吗?或者,在某些语言中,我们称它们是静态方法?那些,不是真的存在于Ruby。在Ruby中,类方法实际上只是在Class对象的单例类中定义的方法。
如果这听起来很疯狂,看看我上面提供的链接。如果你知道如何元编程,很多Ruby的能力只能被挖掘出来 - 在这种情况下你真的想知道单例类/方法,更一般的是Ruby对象模型。
HTH
-Charles
I'm re-defining a method in an object in ruby and I need the new method to be a closure. For example:
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
Now if I define a Proc, it is a closure:
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
Any ideas how to do it?
Thanks.
解决方案This works (tested in irb):
NOTE: This changes only
str
- not all instances of String. Read below for details as to why this worksanother_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 :)
This works because you are opening str's singleton class and defining a method there. Because this, as well as the call to Module#define_method, have what some call a "flat scope", you're able to access variables that would be out of scope if you used
def to_s; 'whatever'; end
.You may want to check out some of these other "metaprogramming spells" here:
media.pragprog.com/titles/ppmetr/spells.pdf
Why does it only changestr
?Because Ruby has a couple interesting tricks up it's sleeves. In the Ruby object model, a method invocation results in the reciever searching not only it's class (and it's ancestors), but also it's singleton class (or as Matz would call it, it's eigenclass). This singleton class is what allows you to [re]define a method for a single object. These methods are called "singleton methods". In the example above, we are doing just that - defining a singleton method name
to_s
. It's functionaly identical to this:def str.to_s ... end
The only difference is that we get to use a closure when calling
Module#define_method
, whereasdef
is a keyword, which results in a change of scope.Why can't it be simpler?
Well, the good news is that you're programming in Ruby, so feel free to go crazy:
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!
And, if you're curious...
You know class methods? Or, in some languages, we'd call them static methods? Well, those don't really exist in Ruby. In Ruby, class methods are really just methods defined in the Class object's singleton class.
If that all sounds crazy, take a look at the links I provided above. A lot of Ruby's power can only be tapped into if you know how to metaprogram - in which case you'll really want to know about singleton classes/methods, and more generally, the Ruby object model.
HTH
-Charles
这篇关于定义一个在Ruby中是闭包的方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!