2

我有一个带有 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 的所有值,一切看起来都很好。我试过没有括号和括号,而不是表达式中的括号,没有任何变化。有什么想法可能导致这种情况吗?

4

2 回答 2

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 有另一个好方法。

于 2017-07-24T20:21:30.033 回答
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.

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

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

其中原始数组被覆盖。


这会处理两次数据。一个更有效的版本是循环遍历数组并将push要保留的元素(适当地更改)到新数组中。

于 2017-07-24T20:21:47.723 回答