下面的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)
什么可能导致这种行为?
使用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)
那么解决方法是什么?
对范围或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)使用临时数组变量
@bar = grep 1, @tmp=@foo[0..9999]
Run Code Online (Sandbox Code Playgroud)(由@FMc建议)用于map设置中间数组
@bar = grep 1, map { $list[$_] } 0..9999;
Run Code Online (Sandbox Code Playgroud)使用数组索引而不是直接使用数组
@bar_indices = grep defined($foo[$_]), 0..9999;
@bar = @foo[@bar_indices];
@bar = @foo[ grep defined($foo[$_]), 0..9999 ];
Run Code Online (Sandbox Code Playgroud)