5

有人可以解释一下,为什么我可以使用$1两次并得到不同的结果?

perl -wle '"ok" =~ /(.*)/; sub { "huh?" =~ /(.*)/; print for @_ }->( "$1", $1 )'

(发现于:如何在 Perl 中排除子匹配?

4

3 回答 3

7

参数数组的@_行为与您想象的不同。子程序中的值@_实际上是真实参数的别名

数组@_ 是一个本地数组,但它的元素是实际标量参数的别名。

当你这样说时:

sub s {
    "huh?" =~ /(.*)/;
    print for @_;
}

"ok" =~ /(.*)/;   
s("$1", $1);

$1一个参数 tos立即由字符串插值计算,但第二个参数不计算,只是注意到子版本中的第二个值@_$1(实际变量$1,而不是它的值)。然后,在里面,你的正则表达式改变了s的值。$1现在,您@_的字符串有一个别名,"ok"后跟一个别名 for $1,这些别名由print循环中的 解析。

如果您将功能更改为:

sub s {
    my @a = @_;
    "huh?" =~ /(.*)/;
    print for @a;
}

甚至这个:

sub s {
    local $1;
    "huh?" =~ /(.*)/;
    print for @_;
}

然后你会得到你期望的两行“ok”。有趣的(有趣的奇特,不好笑的哈哈)是这两个版本s出于不同的原因产生了您的预期结果。该版本在正则表达式使用之前my @a = @_;提取别名的当前值;版本将变量本地化为子,留下别名从子外部引用版本:@_$1local $1;$1@_$1

local 将列出的变量修改为封闭块、文件或 eval 的本地变量。

像这样的怪事就是为什么您应该始终将编号的正则表达式捕获变量的值复制到您的变量中,以及为什么要@_在函数的开头直接解包(除非您知道为什么不想这样做那)。

希望我没有过多地使用术语,这是我一直远离的 Perl 的那些奇怪的角落之一,因为我不喜欢杂耍剃须刀片。

于 2011-05-14T07:17:21.530 回答
2

示例代码利用了两个事实:

  • 数组的元素是@_实际标量参数的别名。特别是,如果一个元素$_[0]被更新,相应的参数也会被更新(反之亦然)。
  • $1是一个全局变量(尽管动态范围为当前 BLOCK),它自动包含()来自上次成功模式匹配的子模式。

子例程的第一个参数是一个普通字符串 ( "ok")。第二个参数是全局变量$1。但是在打印参数之前,子程序内成功的模式匹配会改变它。

于 2011-05-14T07:16:12.483 回答
1

这是因为 perl 通过引用传递参数。

你正在做的类似于:

my $a = 'ok';

sub foo {
  $a = 'huh?';
  print for @_;
}

my $b = $a;
foo($b, $a)

当 sub foo 被调用时,$_[1] 实际上是 $a 的别名,因此它的值在 $a 被修改时被修改。

于 2011-05-14T07:19:48.453 回答