在正则表达式中使用交替时 Perl 未初始化的值
Perl uninitialized value when using alternation in regex
我有一个带有 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 语句的行上显示 "Use of uninitialized value within @array in pattern match...."。
当我从该行的正则表达式中删除交替时,错误消失了。如果我注释掉整个 if 语句,则注释“#Get rid of numbers at the end”下的正则表达式不会产生任何错误。
我已经打印出@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
.
开始from 5.14.0我们可以在替换中使用非破坏性/r
修饰符,returns改变的字符串并留下原来不变。这是一个完美的用例
@array = map { s/_[0-9]+//r } grep { defined && !/_(B|P|BC|PM)/ } @array;
原始数组被覆盖的地方。
这会处理数据两次。一个更有效的版本是遍历数组并push
(复制)要保留的元素,适当地改变,到新数组中。
我有一个带有 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 语句的行上显示 "Use of uninitialized value within @array in pattern match...."。
当我从该行的正则表达式中删除交替时,错误消失了。如果我注释掉整个 if 语句,则注释“#Get rid of numbers at the end”下的正则表达式不会产生任何错误。
我已经打印出@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
.
开始from 5.14.0我们可以在替换中使用非破坏性/r
修饰符,returns改变的字符串并留下原来不变。这是一个完美的用例
@array = map { s/_[0-9]+//r } grep { defined && !/_(B|P|BC|PM)/ } @array;
原始数组被覆盖的地方。
这会处理数据两次。一个更有效的版本是遍历数组并push
(复制)要保留的元素,适当地改变,到新数组中。