【问题标题】:Programmatically Alias Method that uses $& Global Variable以编程方式使用 $& 全局变量的别名方法
【发布时间】:2015-05-28 17:09:42
【问题描述】:

我正在尝试为使用 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 的特殊全局变量一起使用?

【问题讨论】:

  • 您使用的是什么版本的 ruby​​?对我来说,2.2.1 你的例子是有效的。
  • 我使用的是 Ruby 2.2.2。您需要确保在不同的脚本/irb 会话中运行示例,否则您可能正在使用之前定义的方法别名。我在本地机器上尝试了 2.2.1,能够重现该问题。
  • 为什么要使用$& 全局变量?
  • @hakcho ¯_(ツ)_/¯ 但它在野外还活着github.com/rails/rails/blob/master/activesupport/lib/…
  • 我看到了一个更简单的替代您的问题的方法,这就是我问的原因。此外,您可以read 使用它们是不被接受的好习惯。

标签: ruby global-variables metaprogramming


【解决方案1】:

据我所知,你不能这样做。 docs

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

如果您深入研究 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 引发的异常,您可以看到$& 被设置在顶层)

【讨论】:

猜你喜欢
  • 2017-09-19
  • 2018-08-02
  • 1970-01-01
  • 1970-01-01
  • 2023-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-18
相关资源
最近更新 更多