regexperlalternationregex-alternation

Perl uninitialized value when using alternation in regex


I have a for loop with an if statement that looks like this:

   for (my $i=0; $i < $size; $i++) {
       if ($array[$i] =~ m/_(B|P|BC|PM)/) {
           #Remove from @array
           splice(@array, $i, 1);
           next;
       }
       #Get rid of numbers at the end
       $array[$i] =~ s/_[0-9]+//;
   }

I am getting an error saying that says "Use of uninitialized value within @array in pattern match...." on the line with the if statement.

When I remove the alternation from the regex on that line, the error goes away. If I comment out the whole if statement, the regex under the comment "#Get rid of numbers at the end" does not produce any errors.

I've printed out all values of @array and everything looks fine. I've tried no parentheses and brackets instead of the parentheses in the expression with no change. Any ideas what could be causing this?


Solution

  • Here is a simple demonstration of the same problem.

    1: @array = (1,2);
    2: $size = 2;
    3: for ($i=0; $i<$size; $i++) {
    4:    if ($array[$i] == 1) {
    5:        splice @array, $i, 1;
    6:    }
    7: }
    

    So what happens when you execute this code? At line 5, you remove the first element of the array, so the array becomes (2). At the end of the first for-loop iteration, you increment $i (from 0 to 1), compare it to $size (which is still 2), and decide to continue the loop.

    Then you are at line 4 again. You are performing an operation on $array[1]. But @array only has one element, $array[1] is not defined, and Perl gives you a warning.

    It is important to be careful if you are a modifying a data structure at the same time that you are iterating through it.

    --

    Consider this alternate, Perlish approach to the first part of your problem:

    @array = grep { !m/_(B|P|BC|PM)/ } @array
    

    That is, identify all the elements of @array that satisfy some condition (here, the condition is not matching the pattern), and then update @array so that it only holds those good elements. zdim had another good approach.