很长一段时间以来,我总是认为Perl subs中的参数是按值传递的。现在,我遇到了一些我不理解的东西:
use strict;
use warnings;
use Data::Dumper;
sub p {
print STDERR "Before match: " . Data::Dumper->Dump([[@_]]) . "\n";
"1" =~ /1/;
print STDERR "After match: " . Data::Dumper->Dump([[@_]]) . "\n";
}
my $line = "jojo.tsv.bz2";
if ($line =~ /\.([a-z0-9]+)(?:\.(bz2|gz|7z|zip))?$/i) {
p($1, $2 || 'none');
p([$1, $2 || 'none']);
}
首次调用p()时,执行regexp匹配后,@ _中的值将变为undefs。在第二次调用时,一切正常(因为数组引用传递的值不受影响)。
使用Perl版本5.8.8(CentOS 5.6)和5.12.3(Fedora 14)进行测试。
问题是 - 如何发生这种情况,regexp匹配会破坏使用$ 1,$ 2等构建的@_的内容(如果添加它们,其他值不受影响)?
答案 0 :(得分:15)
数组@_是一个本地数组,但它的元素是实际标量参数的别名。
因此,当您将$1
传递给子例程时,该子例程$_[0]
内部是$1
的别名,而不是副本 $1
。因此,它会通过p
中的正则表达式匹配进行修改。
通常,每个Perl子例程的开头应该如下所示:
my @args = @_;
......或者这个:
my ($arg1, $arg2) = @_;
......或者这个:
my $arg = shift;
任何捕获正则表达式都应该像这样使用:
my ($match1, $match2) = $str =~ /my(funky)(regexp)/;
如果没有这些规则,你可能会被微妙的错误驱使。
答案 1 :(得分:4)
正如所建议的那样,复制每个sub中的args是一个好主意(如果只是通过给它们一个非标点符号来记录它们的内容)。
但是,永远不要传递全局变量也是个好主意;传递"$1", "$2"
,而不是$1, $2
。 (这也适用于$DBI::errstr
之类的内容。)
答案 2 :(得分:0)
我不太清楚为什么会这样,但我会说你应该使用类似的东西 我的$ arg1 =班次; 我的$ arg2 =班次; 并在你的sub中使用$ arg1和$ arg2。
使用perl调试器,您将看到@_在2个子调用中看起来不同:
第一个电话:比赛前:
x @_
0 'tsv'
1 'bz2'
比赛结束后:
x @_
0 undef
1 undef
我认为这被比赛覆盖了。
第二个电话:比赛前:
x @_
0 ARRAY(0xc2b6e0)
0 'tsv'
1 'bz2'
比赛结束后:
x @_
0 ARRAY(0xc2b6e0)
0 'tsv'
1 'bz2'
所以也许这不会被覆盖,因为结构不同(?)。
希望这有点帮助。