下面的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"; }
我不认为第一个grep定义是必要的,但它不会造成任何伤害,它应该保证切片之前没有未定义的数组成员.定义的第二个grep是当@list小于5时删除slice结果中的未定义数组成员.
%group已经被重复调用填充:
$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(y的y)的最小值为min(5,y)
什么可能导致这种行为?
解决方法
将grep与数组切片一起使用@list可自动生成元素并扩展数组.
@foo = (1,2,3); @bar = @foo[0..9999]; print scalar @foo; # => 3 @foo = (1,3); @bar = grep 1,@foo[0..9999]; print scalar @foo; # => 10000
这也发生在Perl想要遍历数组切片的其他上下文中.
@foo = (1,3); foreach (@foo[0..9999]) { } print scalar @foo; # => 10000 @foo = (1,3); @bar = map { } @foo[0..9999]; print scalar @foo; # => 10000
那么解决方法是什么?
>对范围或grep操作数使用更复杂的表达式
@bar = grep 1,@foo[0..(@foo>=9999?9999:$#foo)]; @bar = grep 1,@foo>=9999 ? @foo[0..9999] : @foo;
>使用临时数组变量
@bar = grep 1,@tmp=@foo[0..9999]
>(由@FMc建议)使用map来设置一个中间数组
@bar = grep 1,map { $list[$_] } 0..9999;
>使用数组索引而不是直接使用数组
@bar_indices = grep defined($foo[$_]),0..9999; @bar = @foo[@bar_indices]; @bar = @foo[ grep defined($foo[$_]),0..9999 ];