在正则表达式中使用交替时的 Perl 未初始化值



我有一个带有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]+//;
}

我收到一个错误,说"在模式匹配中使用@array内的未初始化值......"在带有 if 语句的行上。

当我从该行的正则表达式中删除交替时,错误消失了。如果我注释掉整个 if 语句,注释"#Get 删除末尾的数字"下的正则表达式不会产生任何错误。

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

这是同一问题的简单演示。

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

从数组中删除元素原则上是昂贵的,即使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(复制(到新数组中。

最新更新