我有一个固定大小的数组,其中数组的大小始终为3.
my @array = ('foo', 'bar', 'qux', 'foo1', 'bar', 'qux2', 3, 4, 5);
Run Code Online (Sandbox Code Playgroud)
我如何聚类数组成员,以便我们可以得到一个数组3的数组:
$VAR = [ ['foo','bar','qux'],
['foo1','bar','qux2'],
[3, 4, 5] ];
Run Code Online (Sandbox Code Playgroud)
Bra*_*ert 30
my @VAR;
push @VAR, [ splice @array, 0, 3 ] while @array;
Run Code Online (Sandbox Code Playgroud)
或者你可以使用natatime从List::MoreUtils
use List::MoreUtils qw(natatime);
my @VAR;
{
my $iter = natatime 3, @array;
while( my @tmp = $iter->() ){
push @VAR, \@tmp;
}
}
Run Code Online (Sandbox Code Playgroud)
我非常喜欢List :: MoreUtils并经常使用它.但是,我从来都不喜欢这个natatime功能.它不会产生可以与for循环或mapor 一起使用的输出grep.
我喜欢在我的代码中链接map/grep/apply操作.一旦你理解了这些功能如何工作,它们就会非常富有表现力并且非常强大.
但很容易使函数像natatime一样工作,返回数组引用列表.
sub group_by ($@) {
my $n = shift;
my @array = @_;
croak "group_by count argument must be a non-zero positive integer"
unless $n > 0 and int($n) == $n;
my @groups;
push @groups, [ splice @array, 0, $n ] while @array;
return @groups;
}
Run Code Online (Sandbox Code Playgroud)
现在你可以做这样的事情:
my @grouped = map [ reverse @$_ ],
group_by 3, @array;
Run Code Online (Sandbox Code Playgroud)
**更新Chris Lutz的建议**
克里斯,我可以看到你建议在界面中添加代码引用的优点.这样就构建了类似地图的行为.
# equivalent to my map/group_by above
group_by { [ reverse @_ ] } 3, @array;
Run Code Online (Sandbox Code Playgroud)
这很简洁.但是为了保持良好的{}代码引用语义,我们将count参数3置于难以看清的位置.
我觉得我最喜欢的东西就像我最初写的那样.
链式映射并不比我们使用扩展API获得的更详细.使用原始方法,可以使用grep或其他类似功能而无需重新实现它.
例如,如果将代码引用添加到API,则必须执行以下操作:
my @result = group_by { $_[0] =~ /foo/ ? [@_] : () } 3, @array;
Run Code Online (Sandbox Code Playgroud)
得到相当于:
my @result = grep $_->[0] =~ /foo/,
group_by 3, @array;
Run Code Online (Sandbox Code Playgroud)
因为我为了容易链接而建议这个,所以我更喜欢原版.
当然,允许任何一种形式都很容易:
sub _copy_to_ref { [ @_ ] }
sub group_by ($@) {
my $code = \&_copy_to_ref;
my $n = shift;
if( reftype $n eq 'CODE' ) {
$code = $n;
$n = shift;
}
my @array = @_;
croak "group_by count argument must be a non-zero positive integer"
unless $n > 0 and int($n) == $n;
my @groups;
push @groups, $code->(splice @array, 0, $n) while @array;
return @groups;
}
Run Code Online (Sandbox Code Playgroud)
现在任何一种形式都应该有效(未经测试).我不确定我是否喜欢原始API,或者这个具有内置地图功能的API更好.
想什么?
**再次更新**
Chris指出可选代码ref版本会强制用户执行以下操作是正确的:
group_by sub { foo }, 3, @array;
Run Code Online (Sandbox Code Playgroud)
哪个不太好,违反了期望.由于没有办法拥有一个灵活的原型(我知道),这使得kibosh在扩展的API上,我坚持使用原始的.
在旁注中,我从备用API中的匿名子开始,但是我将其更改为命名子,因为我对代码的外观略有不满.没有真正的理由,只是直观的反应.我不知道这两种方式是否重要.
小智 5
或这个:
my $VAR;
while( my @list = splice( @array, 0, 3 ) ) {
push @$VAR, \@list;
}
Run Code Online (Sandbox Code Playgroud)
另一个答案(Tore的变种,使用拼接,但避免使用while循环而支持更多的Perl-y映射)
my $result = [ map { [splice(@array, 0, 3)] } (1 .. (scalar(@array) + 2) % 3) ];
Run Code Online (Sandbox Code Playgroud)