在正则表达式

时间:2017-07-24 20:08:31

标签: regex perl alternation regex-alternation

我有一个带有if语句的for循环,如下所示:

   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]+//;
   }

我在if语句的行上收到一条错误,上面写着“在模式匹配中使用@array中未初始化的值....”。

当我从该行上的正则表达式中删除交替时,错误消失了。如果我注释掉整个if语句,那么注释“#Get rid of numbers at the end”下的正则表达式不会产生任何错误。

我已经打印出@array的所有值,一切看起来都很好。我没有尝试括号和括号而不是表达式中的括号而没有任何变化。可能导致这种情况的任何想法?

2 个答案:

答案 0 :(得分:2)

以下是同一问题的简单演示。

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

那么执行此代码会发生什么?在第5行,删除数组的第一个元素,因此数组变为(2)。在第一个for循环迭代结束时,您将$i(从0增加到1),将其与$size(仍为2)进行比较,并决定继续循环。

然后你再次排在第4行。您正在$array[1]执行操作。但是@array只有一个元素,$array[1]没有定义,Perl会给你一个警告。

如果您在迭代数据结构的同时修改数据结构,请务必小心。

-

在问题的第一部分考虑这种替代的Perlish方法:

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

即,识别满足某些条件的@array的所有元素(这里,条件与模式不匹配),然后更新@array以便它只保存那些好的元素。 zdim有另一个好方法。

答案 1 :(得分:2)

从数组中删除元素原则上是昂贵的,即使splice优化有帮助。感谢ysth的评论。更重要的是,通过这些指数正确地工作需要非常小心,因为在暴徒的回答中暴露和解剖。这是另一种方式

my @new_array = 
    map { 
        s/_[0-9]+//;        #/ cleanup from the last statement in loop
        $_                  # return this element, not return of s/../../
    }
    grep { defined && !/_(B|P|BC|PM)/ }  # remove elements
    @array;

首先grep确保跳过undef元素,然后过滤您需要的内容。它的输出列表作为输入传递给map,这使得从循环的最后一行变为每个元素。

如果您不关心旧数组,只需分配到@array而不是@new_array

启动from 5.14.0我们可以在替换中使用非破坏性 /r修饰符,它会返回已更改的字符串并保持原始状态不变。这是一个完美的用例

@array = map { s/_[0-9]+//r } grep { defined && !/_(B|P|BC|PM)/ } @array;

原始数组被覆盖。

这会对数据进行两次处理。更高效的版本是循环遍历数组,并push(复制)要保留(适当更改)的元素到新数组中。