以编程方式使用$&的Alias方法全局变量 [英] Programmatically Alias Method that uses $& Global Variable

查看:173
本文介绍了以编程方式使用$&的Alias方法全局变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试使用Ruby的特殊 $& 返回最后一次正则表达式匹配)。
$ b

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

然而,如果我尝试写一个方法来完成这个操作,它会失败:

  def programatic_alias(klass,method_name)
original = klass.instance_method(method_name)
klass.send :define_method,method_name)do | * args,& block |
putscalled
original.bind(self).call(* args,& block)
end
end

programatic_alias(String, :sub)
foo.sub(/ f /){$& .upcase}
调用
NoMethodError:未定义的方法`upcase'为nil:NilClass
调用
叫做
,从(irb)中调用
:19:在`block in irb_binding'中

看起来全局状态受到 programatic_alias 方法范围的影响,但我不确定这是怎么回事。问题是:我如何以编程方式别名 String#sub ,以便它仍然可以与Ruby的特殊全局变量一起使用?

解决方案

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


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


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



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

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

$ b

示例的一半工作原因是调用 sub 的地方以及引用 $& 的地方(块内)在相同的方法范围内(在这种情况下,红宝石顶级)。将其更改为:

  original = String.instance_method(:sub)
String.send(:define_method,:sub )do | * args,& block |
putscalled
original.bind(self).call(* args,&block)
end

def x
foo .sub(/ f /){$& .upcase}
结束

x()

$& 不再在您的块中定义(如果您捕获 x 你可以看到 $& 被设置在顶层)


I'm trying to alias a method that use's Ruby's special $& (returns last regex match). I can do this manually and it works:

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"

However if I try to write a method that does this for me, it fails:

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'

It looks like the global state is being affected by the scope of the programatic_alias method, but I'm not sure if that's what's going on. The questions is this: how can I programmatically alias String#sub so that it still works with Ruby's special global variables?

解决方案

As far as I know, you can't do this. The docs say

These global variables are thread-local and method-local variables.

If you dig into the ruby source, accessing $& calls last_match_getter which gets its data from rb_backref_get, which calls vm_svar_get which (skipping over a few more internal methods) gets the current control frame and reads the data from there. None of this data is exposed to the ruby api - there's no way to propagate this data from one frame to the one you want to access it in.

In your second example the call to the original method is happening inside your programatic_alias method, and so $& is being set in that scope. For the same reason

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

won't work either.

Your first example half works because the place where sub is called and the place where $& is referenced (inside the block) are in the same method scope (in this case the ruby top level). Change it to:

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()

and $& is no longer defined in your block (if you catch the exception thrown by x you can see that $& is being set at the top level)

这篇关于以编程方式使用$&的Alias方法全局变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆