意外地缩短了Perl切片

sli*_*lim 6 perl list slice

下面的Perl片段应该打印由哈希值引用的数组的前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";
}
Run Code Online (Sandbox Code Playgroud)

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

%groups 已经被重复调用填充:

  $groups{$key} = () unless defined $groups{$key};
  push @{$groups{$key}}, $value;
Run Code Online (Sandbox Code Playgroud)

大多数时候它工作正常:

key1:
   value1
   value2
   value3
   value4
   value5
   (5 of 100)
Run Code Online (Sandbox Code Playgroud)

但有时 - 我在什么情况下没有解决 - 我看到:

key2:
   value1
   (1 of 5)

key3:
   value1
   value2
   (2 of 5)
Run Code Online (Sandbox Code Playgroud)

我期望印刷列表的长度,并x(x of y)min(5,y)

什么可能导致这种行为?

mob*_*mob 8

使用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
Run Code Online (Sandbox Code Playgroud)

这也发生在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
Run Code Online (Sandbox Code Playgroud)

那么解决方法是什么?

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

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

    @bar = grep 1, @tmp=@foo[0..9999]
    
    Run Code Online (Sandbox Code Playgroud)
  3. (由@FMc建议)用于map设置中间数组

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

    @bar_indices = grep defined($foo[$_]), 0..9999;
    @bar = @foo[@bar_indices];
    
    @bar = @foo[  grep defined($foo[$_]), 0..9999 ];
    
    Run Code Online (Sandbox Code Playgroud)