有人可以解释一下,为什么我可以使用$1
两次并得到不同的结果?
perl -wle '"ok" =~ /(.*)/; sub { "huh?" =~ /(.*)/; print for @_ }->( "$1", $1 )'
答案 0 :(得分:7)
@_
参数数组的行为与您的想法不同。子例程中@_
中的值实际上是aliases for the real arguments:
数组@_是一个本地数组,但它的元素是实际标量参数的别名。
当你这样说时:
sub s {
"huh?" =~ /(.*)/;
print for @_;
}
"ok" =~ /(.*)/;
s("$1", $1);
$1
的第一个参数中的s
会立即通过字符串插值进行评估,但第二个参数不会被评估,只需注意子句版本{{1}中的第二个值是@_
(实际变量$1
,而不是它的值)。然后,在$1
内,s
的值会被正则表达式更改。现在,您的$1
包含字符串@_
的别名,后跟"ok"
的别名,这些别名由循环中的$1
解析。
如果将功能更改为:
print
甚至是这样:
sub s {
my @a = @_;
"huh?" =~ /(.*)/;
print for @a;
}
然后你会得到你期待的两行“ok”。有趣的(有趣的,特别的,不好笑的ha-ha)是sub s {
local $1;
"huh?" =~ /(.*)/;
print for @_;
}
这两个版本出于不同的原因产生了你的预期结果。 s
版本在正则表达式开始my @a = @_;
之前,在@_
中提取别名的当前值; local $1;
version将$1
变量本地化为子,将$1
中的别名从子网外引用@_
的版本:
本地修改列出的变量,使其成为封闭块,文件或eval的本地变量。
这样的奇怪之处在于你应该总是尽快将编号的正则表达式捕获变量的值复制到你的变量中,以及为什么要在函数开头解包$1
的原因(除非你知道)为什么你不想这样做。)
希望我没有过多地使用术语,这是Perl的一个奇怪的角落之一,因为我不喜欢玩杂耍的刀片,所以我总是远离它。
答案 1 :(得分:2)
示例代码使用了两个事实:
@_
数组的元素是实际标量参数的别名。特别是,如果更新元素$_[0]
,则更新相应的参数(反之亦然)。$1
是一个全局变量(虽然动态地限定为当前BLOCK),它自动包含来自上一次成功模式匹配的()
的子模式。子例程的第一个参数是普通字符串("ok"
)。第二个参数是全局变量$1
。但是在打印参数之前,子程序内的成功模式匹配会改变它。
答案 2 :(得分:1)
这是因为perl通过引用传递参数。
你在做什么类似于:
my $a = 'ok';
sub foo {
$a = 'huh?';
print for @_;
}
my $b = $a;
foo($b, $a)
当调用sub foo时,$ _ [1]实际上是$ a的别名,所以当$ a被修改时,它的值会被修改。