以编程方式使用$&Global Variable的Alias方法

我正在尝试使用Ruby的特殊$& ( 返回上一个正则表达式匹配 )的方法。 我可以手动完成这个工作:

 original = String.instance_method(:sub) String.send(:define_method, :sub) do |*args, &block| puts "called" original.bind(self).call(*args, &block) end "foo".sub(/f/) { $&.upcase } called # => "Foo" 

但是,如果我尝试编写一个为我执行此操作的方法,它将失败:

 def programatic_alias(klass, method_name) original = klass.instance_method(method_name) klass.send(:define_method, method_name) do |*args, &block| puts "called" original.bind(self).call(*args, &block) end end programatic_alias(String, :sub) "foo".sub(/f/) { $&.upcase } called NoMethodError: undefined method `upcase' for nil:NilClass called called called from (irb):19:in `block in irb_binding' 

看起来全局状态正受到programatic_alias方法范围的影响,但我不确定这是不是正在发生的事情。 问题是这样的:我如何以编程方式为String#sub别名,以便它仍然适用于Ruby的特殊全局变量?

据我所知,你不能这样做。 文档说

这些全局变量是线程局部变量和方法局部变量。

如果你深入了解ruby源代码,访问$&调用last_match_getter ,它从rb_backref_get获取数据,调用vm_svar_get (跳过几个内部方法)获取当前控制帧并从那里读取数据。 这些数据都没有暴露给ruby api – 没有办法将这些数据从一个帧传播到你想要访问它的那个帧。

在第二个示例中,对原始方法的调用发生在programatic_alias方法中,因此在该范围内设置$& 。 出于同样的原因

 'foo'.try(:sub, /f/) {$&.upcase} 

也不会工作。

你的第一个例子是有效的,因为调用sub的地方和$&被引用的地方(在块内)都在同一个方法范围内(在本例中是ruby顶级)。 将其更改为:

 original = String.instance_method(:sub) String.send(:define_method, :sub) do |*args, &block| puts "called" original.bind(self).call(*args, &block) end def x "foo".sub(/f/) { $&.upcase } end x() 

并且$&不再在您的块中定义(如果您捕获x引发的exception,您可以看到$&被设置在顶层)