使用 $& 全局变量的编程别名方法
Programmatically Alias Method that uses $& Global Variable
我正在尝试为使用 Ruby 的特殊 $&
(returns last regex match) 的方法设置别名。我可以手动执行此操作并且有效:
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 的特殊全局变量?
据我所知,你不能这样做。 docs 说
These global variables are thread-local and method-local variables.
如果您深入研究 ruby 源代码,访问 $&
会调用 last_match_getter
which gets its data from rb_backref_get
, which calls vm_svar_get
,后者(跳过更多内部方法)获取当前控制框架并从那里读取数据.此数据的 None 公开给 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
抛出的异常,您可以看到 $&
被设置在顶层)
我正在尝试为使用 Ruby 的特殊 $&
(returns last regex match) 的方法设置别名。我可以手动执行此操作并且有效:
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 的特殊全局变量?
据我所知,你不能这样做。 docs 说
These global variables are thread-local and method-local variables.
如果您深入研究 ruby 源代码,访问 $&
会调用 last_match_getter
which gets its data from rb_backref_get
, which calls vm_svar_get
,后者(跳过更多内部方法)获取当前控制框架并从那里读取数据.此数据的 None 公开给 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
抛出的异常,您可以看到 $&
被设置在顶层)