5

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

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第一次没有设置全局变量,而是第二次设置了全局变量。有什么见解吗?

4

2 回答 2

3

try定义为

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

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

def do_match string, regexp
  string =~ regexp
  $1
end
do_match "Hello World", /^(\w*)/ #=> returns 'Hello'
$1 #=> returns nil 
于 2012-07-11T22:12:13.963 回答
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

这基本上做了什么,只是将方法名称和完整的参数发送到Object. public_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​​ 解释器中的错误吗?. 也许。至少它没有记录在任何官方来源中。我找到了一个参考,告诉以下内容(请参阅全局变量。)

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

所以它似乎$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 错误 #6723

于 2012-07-11T22:12:23.760 回答