如何将Perl数组分区为相同大小的块?

nev*_*int 12 perl

我有一个固定大小的数组,其中数组的大小始终为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)

或者你可以使用natatimeList::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)


dao*_*oad 6

我非常喜欢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中的匿名子开始,但是我将其更改为命名子,因为我对代码的外观略有不满.没有真正的理由,只是直观的反应.我不知道这两种方式是否重要.

  • 为什么不让`group_by`将代码引用作为第一个参数,所以我们可以确定如何处理我们的组?用法:`group_by {[@_]} 3,@ array;` (2认同)

小智 5

或这个:

my $VAR;
while( my @list = splice( @array, 0, 3 ) ) {
    push @$VAR, \@list;
}
Run Code Online (Sandbox Code Playgroud)


DVK*_*DVK 5

另一个答案(Tore的变种,使用拼接,但避免使用while循环而支持更多的Perl-y映射)

my $result = [ map { [splice(@array, 0, 3)] } (1 .. (scalar(@array) + 2) % 3) ];
Run Code Online (Sandbox Code Playgroud)

  • 我不会因为它使用`map()`而将它称为Perl-y - 它在很大程度上更混乱,更难以理解.最"Perl-y"的解决方案是`natatime()`,因为它来自CPAN. (2认同)
  • 嗯......我不能说我非常不同意你的观点:可能更难以理解.但是多年来我一直是一名专业的Perl开发人员,我在CPAN上遇到了一些难以置信的垃圾,我不一定认为"使用CPAN的东西"是一个批准perl解决方案的好家庭密封.请注意,List :: MoreUtils,从我今天的粗略检查,看起来是一个非常整洁和有用的模块,所以它绝对不包括在上面的抱怨:) (2认同)
  • 抱歉.经过2个不眠之夜,我的讽刺模块没有加载. (2认同)