【问题标题】:How can I loop over an array from the first occurrence of an element with a specific value using perl?如何使用 perl 从第一次出现具有特定值的元素开始遍历数组?
【发布时间】:2012-02-01 13:51:28
【问题描述】:

我有一个类似 ("valueA", "valueB", "valueC", "valueD") 等的数组。我想从(例如)“valueC”的第一个实例开始循环遍历数组的值.值“valueC”的第一个实例之前的数组中的所有内容都应被忽略;所以在这种情况下,只有“valueC”和“valueD”会被循环处理。

我可以在循环中放置一个条件,但是有没有更简洁的方式来使用 perl 来表达这个想法?

【问题讨论】:

    标签: arrays loops perl


    【解决方案1】:
    my $seen;
    for ( grep $seen ||= ($_ eq "valueC"), @array ) {
        ...
    }
    

    【讨论】:

    • 与 Perl 的大部分内容一样,有点简洁。想解释一下这是在做什么以及为什么会起作用?
    • $seen 变量用作标志:我已经看到起始值了吗? grep 遍历数组,如果还没有看到,它将比较的结果分配给看到。这样,在第一次出现 valueC 时, seen 将设置为 1,以后不会更改。 Grep 将仅返回来自 @array$seen 为真的值,即 1。
    • @DanFego :也可以写成grep { $seen = $seen || $_ eq 'valueC' } @array。一旦 $seen 设置为 true,它就保持为 true。
    【解决方案2】:

    我认为您还需要检查数组中是否存在“valueC”。
    希望这会有所帮助。

    use strict;
    use warnings;
    use List::Util qw(first);
    
    my @array = qw(valueA valueB valueC valueD);
    
    my $starting_element = 'valueC';
    
    # make sure that the starting element exist inside the array
    # first search for the first occurrence of the $stating_element
    # dies if not found
    my $starting_index = first { $array[$_] eq $starting_element } 0 .. $#array
        or die "element \"$starting_element\" does not exist inside the array";
    
    # your loop
    for my $index ($starting_index .. $#array) {
        print $array[$index]."\n";
    }
    

    【讨论】:

      【解决方案3】:
      my $seen;
      for ( @array ) {
      
          $seen++ if /valueC/;
          next unless $seen;
          ...
      }
      

      但是$seen 有点笨拙。 flip-flop operator 看起来更整洁 IMO:

      for ( @array ) {
      
          next unless /^valueC$/ .. /\0/;
                 # or /^valueC$/ .. '' !~ /^$;
                 # or $_ eq 'valueC' .. /\0/;
          ...
      }
      

      或者简单地说(基于 ikegami 的建议):

      for ( grep { /^valueC$/ .. /(*FAIL)/ } @array ) { ... }
      

      【讨论】:

      • 如果你想要一些永远不会匹配的东西,/(?!)//(*FAIL)/ 是理想的,但你实际上想要一些不是数字常量的 false,所以 do{0} 可以。
      • @ikegami :谢谢,我希望对此有所启发。我有点惊讶 sub{ 0 }!! 0 不起作用。
      • @Zaid, sub { 0 } 返回一个正确的代码引用——sub { 0 }->() 可以工作——而!!0 是一个常量。
      【解决方案4】:
      use List::MoreUtils qw( first_index );
      
      foreach my $item ( @array[ ( first_index { $_ eq 'ValueC' } @array ) .. $#array ] ){
          # process $item
      }
      

      【讨论】:

        【解决方案5】:
        my $start = 0;
        ++$start while $start < @array && $array[$start] ne 'valueC';
        

        后跟

        for (@array[$start..$#array]) {
           say;
        }
        

        for my $i ($start..$#array) {
           say $array[$i];
        }
        

        【讨论】:

          【解决方案6】:

          TIMTOWTDI,但我认为:

          foreach my $item (@list) {
                next if !$seen && ($item ne 'valueC');
                $seen++;
                ...
          }
          

          既可读、正确又简洁。所有 /valueC/ 解决方案都将处理“DooDadvalueCFuBAr”之后的任何内容,而不是 OP 要求的内容。而且,不,您不需要触发器/范围运算符,并且事先检查是否存在真的很奇怪,除了需要一个可能的非核心包来执行一项相当微不足道的任务。 grep 解决方案真的让我头晕目眩,除了创建和折腾一个临时数组作为副作用。

          如果你想花哨并避免''ifs':

          foreach my $item (@list) {
               $seen || ($item eq 'valueC') || next;
               $seen++;
               ...
          }
          

          只是不要写回家。 :-)

          【讨论】:

          • 没有/valueC/,触发器仍然可以工作:next unless $_ eq 'valueC' .. '' !~ /^$/; 这个想法是引入触发器结构作为 TIMTOWDI 替代方案,仅此而已。
          • 对于使用 strict,只需在适当的地方(在循环之前)添加 ''my $seen=0;"'' 等。结构成立。
          • 您不需要my $seen=0;,因为my $seen; 也可以。
          • next if !$seen || ($item ne 'valueC') 有效吗?我认为这总是会给你一个真正的价值。
          猜你喜欢
          • 2011-12-14
          • 1970-01-01
          • 2020-11-26
          • 2011-01-21
          • 1970-01-01
          • 2019-02-09
          • 2011-05-24
          • 1970-01-01
          • 2015-03-31
          相关资源
          最近更新 更多