【问题标题】:Perl uninitialized value when using alternation in regex在正则表达式中使用交替时 Perl 未初始化的值
【发布时间】:2017-07-24 20:08:31
【问题描述】:

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

【问题讨论】:

  • “删除交替”是指我将正则表达式更改为“$array[$I] =~ m/_B/”
  • 哦,是的,我每次都需要减少 $size,谢谢
  • 仅仅减少 $size 是不够的;删除元素后,您仍将跳过处理该元素
  • 你还需要递减$i
  • @mob 或重做而不是下一个

标签: regex perl alternation regex-alternation


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    从数组中删除元素原则上是昂贵的,尽管splice 优化有帮助。感谢ysth cmets。更重要的是,通过这些索引正确工作需要非常小心,正如暴民的回答中所揭示和剖析的那样。这是另一种方式

    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(复制)要保留的元素,适当更改,到新数组中。

    【讨论】:

    • 从数组中删除元素并不是那么昂贵。它的费用与剩余元素的数量有关,而不是与这些元素的大小有关。
    • 另外,从 5.14 开始你可以做... = map s/_[0-9]+//r, grep ...
    • @ysth 好吧,当然。我正在考虑一个任意数组,最终可能会从中间删除很多很多元素。我不知道可以进行哪些优化(特别是splice),但我害怕数据复制(完整的数组分配)可能会按照数组大小的顺序进行多次。随着我们的发展,它会减少,这只是最坏的情况,但我仍然不喜欢复杂性。
    • @ysth 啊,当然!我只是在另一个答案中使用它...谢谢,添加:)
    • push/pop/shift/unshift/splice 都经过了很好的优化,尽可能少地移动指向元素的指针,并且它们根本不会触及剩余的实际数据
    猜你喜欢
    • 2011-11-15
    • 2011-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-14
    • 2016-09-25
    • 2013-05-28
    相关资源
    最近更新 更多