6

我正在学习 Ruby (2.0),这让我很惊讶:

s = "1234"
s =~ /\d+/
$&  ==> "1234"       # as expected, $& contains the matched string
$&.slice!(-2..-1)    # should mutate string
$&  ==> "1234"       # what?
s.slice(-2..-1) 
s  ==> "12"          # as expected

slice!方法应该改变字符串。其他 mutator 方法的行为方式相同。我的问题:为什么这不会引发错误,这是我所期望的,当一个函数不能做它说它会做的事情时?这是在某处记录的吗?有道理吗?

更新

所以,我看到它$&不像一个全局变量。对它的每个引用都会给出一个新对象,就好像它真的是一个无参数函数一样:

irb> $foo = "1234"
=> "1234"
irb> $foo.object_id
=> 70205012205980
irb> $foo.object_id
=> 70205012205980   # the same
irb> $&.object_id
=> 70205003531300
irb> $&.object_id   
=> 70205011619040   # different object

所以......我的问题变成了:这只是解释器的“魔法”,还是$&实际上是一个无参数函数,就像我可以在 Ruby 中使用def ... end? 而且,我怎么能分辨出来呢?foo在 Python 中,我可以通过使用它的名称来引用一个函数:

>>> foo
<function foo at 0x10d3117d0>

有没有办法在 Ruby 中做到这一点?然后我可以看看 $& “真的”是什么(如果它不是魔法的话)。

4

3 回答 3

5

Ruby C API 包括挂钩变量和虚拟变量。从README.EXT

您可以定义挂钩变量。在访问挂钩变量时调用访问器函数(getter 和 setter)。

void rb_define_hooked_variable(const char *name, VALUE *var,
           VALUE (*getter)(), void (*setter)())

如果您需要提供 setter 或 getter,只需为不需要的钩子提供 0。如果两个钩子都是 0,rb_define_hooked_variable() 就像 rb_define_variable() 一样工作。

getter 和 setter 函数的原型如下:

VALUE (*getter)(ID id, VALUE *var);
void (*setter)(VALUE val, ID id, VALUE *var);

您也可以定义一个没有相应 C 变量的 Ruby 全局变量。变量的值只能通过钩子来设置/获取。

 void rb_define_virtual_variable(const char *name,
            VALUE (*getter)(), void (*setter)())

getter 和 setter 函数的原型如下:

VALUE (*getter)(ID id);
void (*setter)(VALUE val, ID id);

$&是一个虚变量的例子,它定义在 中re.c,对应的 getter 是last_match_getter并且没有 assocoat setter。

在某种意义上,一个无参数函数也是如此$&,只是它是在 C 中实现的。你不能(据我所知)在纯 Ruby 中定义你自己的虚拟全局变量(如果你创建你的自己的 C 扩展)。

于 2013-02-28T21:08:18.007 回答
2

由于 的目的$&是告诉您匹配了什么,因此匹配应该是唯一可以更新它的操作是有意义的。

于 2013-02-28T17:43:59.453 回答
1

这是个有趣的问题。jeconner 的回答和对该问题的许多评论只是说这样做没有用,但这并没有回答问题。无论是否应该在实际代码中完成,OP 的问题仍然存在。

问题的答案是,这看起来像是只读字符串的功能。当您对只读字符串应用破坏性操作时,它会返回一个不同的字符串对象。我同意 OP 这很奇怪。它应该引发错误。

除非有人提出令人信服的解释,否则我建议您在 Ruby 核心上询问它,和/或请求一个功能来引发错误。

于 2013-02-28T18:49:11.633 回答