Perl - 意外行为:如果迭代器 $r 通过正则表达式更改,则迭代数组“foreach $r (@a)” - 数组元素本身已更改

pkr*_*171 2 regex perl

sub summs_by_key {
    foreach my $R (@CURR_RECS) {    
        if ($combine) {
            print __LINE__ . ") ... $R" ; 
            $R =~ s/\^/\|/  for 1 .. $KEY_POS ; # the regex expression
            print __LINE__ . ") ___ $R" ; 
        }
        #... etc 
    }
    #... etc // no other reassingments of $R 
}
Run Code Online (Sandbox Code Playgroud)

注意: @CURR_RECS 是一个声明为的全局数组,our @CURR_RECS ; 它在单独的子/函数中填充。正则表达式的更改实际上可以更改数组值。该函数被调用多次,在后续调用中,我们注意到正则表达式的更改已作用于数组的元素。

我的问题是这是正常行为还是 Perl 错误 - 它仅在正则表达式行执行 2 次而不是一次时发生(即 $KEY_POS = 2)

我通过不直接使用迭代器而是使用它的“副本”来解决这个问题。我将迭代器重命名为 $Rs 并将其分配给 $R。

foreach my $Rs (@CURR_RECS) {
        my $R = $Rs ;
        if ($combine) {
            print __LINE__ . ") ... $R" ; 
            $R =~ s/\^/\|/  for 1 .. $KEY_POS ;
            print __LINE__ . ") ___ $R" ; 
        }
        # ... etc 
}
Run Code Online (Sandbox Code Playgroud)

@CURR_RECS 包含

sub summs_by_key {
    foreach my $R (@CURR_RECS) {    
        if ($combine) {
            print __LINE__ . ") ... $R" ; 
            $R =~ s/\^/\|/  for 1 .. $KEY_POS ; # the regex expression
            print __LINE__ . ") ___ $R" ; 
        }
        #... etc 
    }
    #... etc // no other reassingments of $R 
}
Run Code Online (Sandbox Code Playgroud)

其中插入号是字段分隔符。如果 $combine 为 true,那么我们想要组合前这么多字段...这就是正则表达式所完成的...通过将如此多的 ($KEY_POS) 分隔符更改为其他内容 - 更改为“|”。这些是正则表达式条目之前和之后的内容:

foreach my $Rs (@CURR_RECS) {
        my $R = $Rs ;
        if ($combine) {
            print __LINE__ . ") ... $R" ; 
            $R =~ s/\^/\|/  for 1 .. $KEY_POS ;
            print __LINE__ . ") ___ $R" ; 
        }
        # ... etc 
}
Run Code Online (Sandbox Code Playgroud)

然而,当再次执行相同的函数时,传入的元素与第一次运行时不同......但已根据正则表达式进行了修改。前导 126) ...127) ___ 不是数组的一部分,它们是代码的行号。在第二次调用该函数时,我们可以看到传入的元素显示了先前调用移动的插入符号。

2x^szo_p^230414:01:43^0^0^
3x^cold^230309p^0^0^QQ.y
3x^szo_p^230419:14:51^-10^6^
4x^cold^230320^0^60^
4x^front^230418:16:36^20^40^
4x^sky^230403^0^0^
4x^szo_r^230419:14:46^-5^23^
6g^szo_p^230309^0^8^
6x^cup^230417:05:04^2^4^
6x^r_l^230402^0^40^
Run Code Online (Sandbox Code Playgroud)

ike*_*ami 5

佩尔辛:

如果 LIST 的任何元素是左值,则可以通过修改循环内的 VAR 来修改它。相反,如果 LIST 的任何元素不是左值,则任何修改该元素的尝试都将失败。换句话说,foreach 循环索引变量是您要循环的列表中每个项目的隐式别名。

所以是的,循环迭代器不是副本,而是别名。

my @a = 1..3;
++$_ for @a;
say "@a";  # "2 3 4"
Run Code Online (Sandbox Code Playgroud)

以下是制作副本的稍微简单的方法:

for ( @CURR_RECS ) {
   my $R = $_;
   ...
}
Run Code Online (Sandbox Code Playgroud)

以下是另一种方法,但您可能应该避免,因为它的作用并不明显:

for my $R ( map $_, @CURR_RECS ) {
   ...
}
Run Code Online (Sandbox Code Playgroud)

您也可以避免复印。

my $R2 = join "|", split /\^/, $R, $KEY_POS+1;
print __LINE__ . ") ___ $R2";
Run Code Online (Sandbox Code Playgroud)

当然也比多次替换要快。