意外地缩短了Perl切片

下面的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 ];

相关文章

1. 如何去重 #!/usr/bin/perl use strict; my %hash; while(...
最近写了一个perl脚本,实现的功能是将表格中其中两列的数据...
表的数据字典格式如下:如果手动写MySQL建表语句,确认麻烦,...
巡检类工作经常会出具日报,最近在原有日报的基础上又新增了...
在实际生产环境中,常常需要从后台日志中截取报文,报文的形...
最近写的一个perl程序,通过关键词匹配统计其出现的频率,让...