2
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 
}

Notes: @CURR_RECS is a global array declared as our @CURR_RECS ; It is filled up in a separate sub/function. The regex change actually can change the array values. This function is called several times and on subsequent calls the regex change is noticed to have acted on the elements of the array.

My question is whether that is normal behavior or a Perl bug - it only happens when the regex line is executed 2 times, not once (i.e. $KEY_POS = 2)

I fixed the problem by not using the iterator directly but a "copy" of it. I renamed the iterator to $Rs and assigned it to $R.

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

@CURR_RECS contains

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^

where the carets are field separator markers. If $combine is true then we want to combine the first so many fields ... that is what the regex accomplishes ... by changing so many ($KEY_POS) of the separators to something else - into "|". So these are the before and after the regex entries:

125) ... 2m^szo_p^230411:15:01^0^0^
127) ___ 2m|szo_p|230411:15:01|0^0^
125) ... 2x^szo_p^230414:01:43^0^0^
127) ___ 2x|szo_p|230414:01:43|0^0^
125) ... 3x^cold^230309p^0^0^QQ.y
127) ___ 3x|cold|230309p|0^0^QQ.y
125) ... 3x^szo_p^230419:14:51^-10^6^
127) ___ 3x|szo_p|230419:14:51|-10^6^

However when the same function is executed again the incoming elements are not the same as in the first run ... but have been modified per the regex. The leading 126) ... and 127) ___ are not part of the array they are LINE numbers of the code. ON the second calling of the function we can see that the incoming elements show the carets moved by the earlier invocation.

121) ... 2m|szo_p|230411:15:01|0^0^
123) ___ 2m|szo_p|230411:15:01|0|0|
Use of uninitialized value $V in addition (+) at /media/pk/u2win/dev/pkp.db/pkp.summ.pl line 132.
121) ... 2x|szo_p|230414:01:43|0^0^
123) ___ 2x|szo_p|230414:01:43|0|0|
Use of uninitialized value $V in addition (+) at /media/pk/u2win/dev/pkp.db/pkp.summ.pl line 132.
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • You can do `$R =~ s/\^/\|/ for 0 .. $KEY_POS ;` – vkk05 Apr 20 '23 at 05:08
  • Thank you... that is a useful observation and I'll keep that modification in the code. However, the behavior stayed the same when using the iterator directly (without the extra variable assignment). – pkrauss6171 Apr 20 '23 at 05:35
  • Thank you for that observation, I made (and will keep) that change. The behaviour is not changed though. It seems like one has to make the extra variable assignment and not modify the iterator variable itself. This is what I got out of another post - the iterator variable is an alias to the array element. https://stackoverflow.com/questions/72645500/perl-substitution-to-foreach-local-variable-changed-the-elements-of-array-use?rq=2 – pkrauss6171 Apr 20 '23 at 06:14

1 Answers1

5

perlsyn:

If any element of LIST is an lvalue, you can modify it by modifying VAR inside the loop. Conversely, if any element of LIST is NOT an lvalue, any attempt to modify that element will fail. In other words, the foreach loop index variable is an implicit alias for each item in the list that you're looping over.

So yes, the loop iterator isn't a copy, but an alias.

my @a = 1..3;
++$_ for @a;
say "@a";  # "2 3 4"

The following is a slightly simpler way of making it a copy:

for ( @CURR_RECS ) {
   my $R = $_;
   ...
}

The following is another way, but you should probably avoid since it's not obvious what it does:

for my $R ( map $_, @CURR_RECS ) {
   ...
}

You could also avoid making a copy.

my $R2 = join "|", split /\^/, $R, $KEY_POS+1;
print __LINE__ . ") ___ $R2";

Surely faster than multiple substitutions, too.

ikegami
  • 367,544
  • 15
  • 269
  • 518