在方法定义中使用$ 1,$ 2等全局变量

Bog*_*iev 8 ruby scope pseudo-globals

给出以下两段代码:

def hello(z)
  "hello".gsub(/(o)/, &z)
end
z = proc {|m| p $1}
hello(z)
# prints: nil
Run Code Online (Sandbox Code Playgroud)
def hello
  z = proc {|m| p $1}
  "hello".gsub(/(o)/, &z)
end
hello
# prints: "o"
Run Code Online (Sandbox Code Playgroud)

为什么这两段代码的输出不同?有没有一种方法,以块传递到gsub从方法定义之外,这样的变量$1,$2将被以同样的方式,就好像块的方法定义中给出的评价?

fot*_*nus 4

\n

为什么输出不同?

\n
\n

ruby 中的过程有词法作用域。这意味着当它找到未定义的变量时,它会在定义过程的上下文中解析,而不是调用。这解释了您的代码的行为。

\n

您可以看到该块是在正则表达式之前定义的,这可能会导致混乱。该问题涉及一个神奇的 ruby​​ 变量,它的工作方式与其他变量完全不同。引用@J\xc3\xb6rgWMittag

\n
\n

实际上,这相当简单: $SAFE 的行为与您对全局变量的期望不同的原因是因为它不是全局变量。这是一个神奇的独角兽。

\n

Ruby 中有相当多的神奇独角兽,不幸的是,它们没有很好的文档记录(事实上,根本没有文档记录),因为替代 Ruby 实现的开发人员很难发现。这些东西的行为都不同并且(看起来)不一致,而且它们几乎唯一的两个共同点是它们看起来像全局变量,但行为却不像它们。

\n

有些具有本地范围。有些具有线程本地范围。有些会神奇地发生变化,而无需任何人分配给它们。有些对于解释器来说具有神奇的意义,可以改变语言的行为方式。有些还附加了其他奇怪的语义。

\n
\n

如果您真的想确切地了解$1$2变量如何工作,我假设您会找到的唯一“文档”是ruby​​spec,这是由 Rubinus 人员艰难完成的 ruby​​ 规范。祝你黑客攻击愉快,但要做好承受痛苦的准备。

\n
\n
\n

有没有办法以正确的方式将 $1、$2 变量设置从另一个上下文传递给 gsub 块?

\n
\n

您可以通过以下修改实现您想要的效果(但我打赌您已经知道了)

\n
require \'pp\'\ndef hello(z)\n  #z = proc {|m| pp $1}\n  "hello".gsub(/(o)/, &z)\nend\nz = proc {|m| pp m}\nhello(z)\n
Run Code Online (Sandbox Code Playgroud)\n

我不知道如何动态更改过程的范围。但你真的想这样做吗?

\n

  • @BogdanGusiev `$` 变量不像普通变量或全局变量那样工作 - 检查[这个答案](http://stackoverflow.com/a/2279980/413494) (2认同)