【问题标题】:Unexpectedly short Perl slices出乎意料的短 Perl 切片
【发布时间】:2013-05-02 21:05:47
【问题描述】:

Perl 的以下 sn-p 应该打印由哈希值引用的数组的前 5 项,如果数组较短,则打印更少。

while ( my ($key,$value) = each %groups ) {
   print "$key: \n";
   my @list = grep defined, @{$value};
   my @slice = grep defined, @list[0..4];
   foreach my $item ( @slice ) {
      print "   $item \n";
   }
   print "   (", scalar @slice, " of ", scalar @list, ")\n";
}

我不认为第一个grep defined是必要的,但它不会造成任何伤害,它应该保证切片之前没有未定义的数组成员。第二个grep defined是当@list小于5时,删除slice结果中未定义的数组成员。

%groups 已通过重复调用填充:

  $groups{$key} = () unless defined $groups{$key};
  push @{$groups{$key}}, $value;

大部分时间都可以正常工作:

key1:
   value1
   value2
   value3
   value4
   value5
   (5 of 100)

但有时——我还没有弄清楚在什么情况下——我明白了:

key2:
   value1
   (1 of 5)

key3:
   value1
   value2
   (2 of 5)

我希望打印列表的长度,以及来自(x of y)xmin(5,y)

什么可能导致这种行为?

【问题讨论】:

    标签: perl list slice


    【解决方案1】:

    grep@list 的数组切片一起使用会自动激活元素并扩展数组。

    @foo = (1,2,3);
    @bar = @foo[0..9999];
    print scalar @foo;             # =>  3
    
    @foo = (1,2,3);
    @bar = grep 1, @foo[0..9999];
    print scalar @foo;             # => 10000
    

    这也发生在 Perl 想要遍历数组切片的其他上下文中。

    @foo = (1,2,3);
    foreach (@foo[0..9999]) { }
    print scalar @foo;             # => 10000
    
    @foo = (1,2,3);
    @bar = map { } @foo[0..9999];
    print scalar @foo;             # => 10000
    

    那么解决方法是什么?

    1. 对范围或grep 操作数使用更复杂的表达式

      @bar = grep 1, @foo[0..(@foo>=9999?9999:$#foo)];
      @bar = grep 1, @foo>=9999 ? @foo[0..9999] : @foo;
      
    2. 使用临时数组变量

      @bar = grep 1, @tmp=@foo[0..9999]
      
    3. (@FMc 建议)使用map 设置中间数组

      @bar = grep 1, map { $list[$_] } 0..9999;
      
    4. 使用数组索引而不是直接使用数组

      @bar_indices = grep defined($foo[$_]), 0..9999;
      @bar = @foo[@bar_indices];
      
      @bar = @foo[  grep defined($foo[$_]), 0..9999 ];
      

    【讨论】:

    • 哎哟!有没有一种惯用的(或至少简洁的)方法?
    • 我正在考虑。不幸的是,no autovivification 没有帮助。
    • print " (", scalar @slice, " of ", scalar (grep defined, @list), ")\n"; 实现正确的输出。不过可能会给未来的维护者留下一个陷阱。
    • my @slice = grep defined, map $list[$_], 0..3;
    • 我通常不会在发布后的 11 分钟内接受答案,但我认为在这种情况下当之无愧。谢谢。
    猜你喜欢
    • 2014-10-02
    • 2014-09-11
    • 2013-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-01
    • 2014-09-19
    相关资源
    最近更新 更多