我有一个带有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的所有值,一切看起来都很好。我没有尝试括号和括号而不是表达式中的括号而没有任何变化。可能导致这种情况的任何想法?
答案 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
(复制)要保留(适当更改)的元素到新数组中。