【问题标题】:Regex global variables are not being set未设置正则表达式全局变量
【发布时间】:2012-07-11 21:29:07
【问题描述】:

我遇到了一些看起来不寻常的事情,我想知道是否有人可以解释原因。

1.8.7 :001 > some_str = "Hello World"
 => "Hello World" 
1.8.7 :002 > some_str.try(:match, /^(\w*)/)
 => #<MatchData "Hello" 1:"Hello"> 
1.8.7 :003 > $1
 => nil 
1.8.7 :004 > some_str.match(/^(\w*)/)
 => #<MatchData "Hello" 1:"Hello"> 
1.8.7 :005 > $1
 => "Hello" 

我不确定为什么全局变量$1 不是第一次设置,而是第二次设置。有什么见解吗?

【问题讨论】:

  • 您应该在其中添加 rails 或 active-support 标签。 try 没有内置在 Ruby 中,也不常用在 Rails 之外
  • $1 不是全局变量。它是按线程定义的。

标签: ruby-on-rails ruby regex activesupport


【解决方案1】:

让我告诉你try是如何实现的。如果您想自己查看,请查看 activesupport 源。定义在/lib/active_support/core_ext/object/try.rb

class Object
  def try(*a, &b)
    if a.empty? && block_given?
      yield self
    else
      public_send(*a, &b)
    end
  end
end

这基本上是做的,只是将方法名称和完整的参数发送到Objectpublic_send 与 send 相同,但只能用于调用公共方法。

所以我重写了这个来调试你的问题:

class Object
  def try(*a)
    result = public_send(*a)
    puts $1.inspect
    result
  end
end

string = "Hello"
string.try(:match, /^(\w*)/)
puts $1.inspect

这个输出

"Hello"
nil

所以出现了一个很好的问题:这是 ruby​​ 解释器中的错误吗?也许。 至少它没有记录在任何官方来源中。我找到了一个参考资料,其中包含以下内容 (See Global variables.)

[...]、$_$~ 具有本地范围。它们的名称表明它们应该是全球性的,但这种方式更有用,而且使用这些名称是有历史原因的。

所以看起来$1 也不是全局变量,即使它被内核报告为全局变量:

1.9.3-p194 :001 > global_variables
 => [:$;, :$-F, :$@, :$!, :$SAFE, :$~, :$&, :$`, :$', :$+, :$=, :$KCODE, :$-K,
     :$,, :$/, :$-0, :$\, :$_, :$stdin, :$stdout, :$stderr, :$>, :$<, :$.,
     :$FILENAME, :$-i, :$*, :$?, :$$, :$:, :$-I, :$LOAD_PATH, :$",
     :$LOADED_FEATURES, :$VERBOSE, :$-v, :$-w, :$-W, :$DEBUG, :$-d, :$0,
     :$PROGRAM_NAME, :$-p, :$-l, :$-a, :$binding, :$1, :$2, :$3, :$4, :$5, :$6,
     :$7, :$8, :$9] 

为了确保这一点,我将这个不一致的地方转发给了 Ruby Bug Tracker。见Ruby Bug #6723

【讨论】:

    【解决方案2】:

    try 定义为

    def try(method, *args, &block)
      send(method, *args, &block)
    end
    

    当然,在 nil 上它只返回 nil。为什么这很重要?因为正则表达式全局变量不是真正的全局变量:它们是在每个方法和每个线程的基础上维护的(通过阅读 ruby​​ 源代码很容易看到这一点)。当您通过try 调用match 时,全局变量设置在try 的范围内,但在下一种情况下,它们设置在顶层。这很容易验证

    def do_match string, regexp
      string =~ regexp
      $1
    end
    do_match "Hello World", /^(\w*)/ #=> returns 'Hello'
    $1 #=> returns nil 
    

    【讨论】:

    • 我建议不要使用全局变量,除非你必须使用(subgsubscan、...块),MatchData 实例提供了更好的接口,甚至处理命名的捕获组。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-05
    • 1970-01-01
    相关资源
    最近更新 更多