正在设置正则表达式全局变量

时间:2012-07-11 21:29:07

标签: ruby-on-rails ruby regex activesupport

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

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,而是设置第二次。任何见解?

2 个答案:

答案 0 :(得分:3)

try定义为

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

当然除了nil之外它只返回nil。为什么这很重要?因为正则表达式全局变量不是真正的全局变量:它们是基于per方法和每个线程维护的(通过仔细阅读ruby源很容易看到它)。当您通过match致电try时,全局变量将设置在try的范围内,但在下一种情况下,它们将设置在顶层。这很容易验证

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

答案 1 :(得分:3)

让我告诉你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