定义一个Ruby中的闭包方法

pup*_*eno 9 ruby closures

我正在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
Run Code Online (Sandbox Code Playgroud)

现在,如果我定义一个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
Run Code Online (Sandbox Code Playgroud)

有什么想法怎么做?

谢谢.

Cha*_*les 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 :)
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为你正在打开str的单例类并在那里定义一个方法.因为这个,以及对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
Run Code Online (Sandbox Code Playgroud)

唯一的区别是我们在调用时使用闭包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!
Run Code Online (Sandbox Code Playgroud)

而且,如果你很好奇......

你知道班级方法吗?或者,在某些语言中,我们称之为静态方法?那么,那些不真正存在的红宝石.在Ruby中,类方法实际上只是在Class对象的singleton类中定义的方法.

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

HTH

- 查尔斯


Cha*_*les 9

在Ruby 1.9.2中实现的功能#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
Run Code Online (Sandbox Code Playgroud)

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

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