在特定位置拆分数字数组

时间:2014-06-20 16:43:58

标签: arrays perl reference split

我试图将数值数组拆分成更小的数组,这样每个较小的数组都不能包含任何不同的数字。

示例:数组(2,2,2,2,2,9,3,3,3,3)应分为三个数组(2,2,2,2,2)(9)(3,3,3,3)

以下是我的尝试:

my @arr = (2,2,2,2,2,9,3,3,3,3);

my @result = ();
my $last = -1;
my @newarr = ();

for my $i (0 .. $#arr){
        if ( ($i>0 && $arr[$i] != $last) || $i == $#arr ){
                push @result, \@newarr;
                @newarr = ();
        }
        $last = $arr[$i];
        push @newarr, $arr[$i];
}

首先,这段代码没有给我想要的结果。我认为我的错误是当我将@newarr的引用推送到@result时,然后我重新初始化@newarr

其次,还有更优雅的方法吗?我查看了函数splitsplice,但想不到一个好的解决方案。

7 个答案:

答案 0 :(得分:5)

List :: MoreUtils具有“part”功能:

use Data::Dumper;
use feature 'state';
use List::MoreUtils 'part';

my @array = ( 2,2,2,2,2, 9, 3,3,3,3 );

my @part = part {
  state $prev;
  state $i = -1;
  $i++ if !defined($prev) || $_ ne $prev;
  $prev = $_;
  $i
} @array;

print Dumper @part;

使用'part',代码块返回的值指示顶级数组索引,其中当前值将被推送到匿名数组中。 $prev开始是未定义的,因此输入中的第一个元素会触发$i增加到0,因此所有'2'都会以@{$part[0]}结尾。只要@array中的元素与$ prev不匹配,索引就会递增,后续元素会以@{$part[1]}结尾。每次检测到更改时,都会开始新的分组。

<强>更新

如果这段代码可能被多次使用,那么“状态”变量将在调用之间保持其值。在这种情况下,状态比它的价值更麻烦,人们应该在子例程中使用词法:

use Data::Dumper;
use List::MoreUtils 'part';

my @array = ( 2,2,2,2,2, 9, 3,3,3,3 );
my @part = partition(@array);
print Dumper \@part;

sub partition {
  my( $prev, $i ) = ( undef, -1 );
  return part {
    $i++ if ! defined($prev) || $_ ne $prev;
    $prev = $_;
    $i;
  } @_;
}

答案 1 :(得分:3)

创建按相似元素分组的数组数组。

要复习复杂的数据结构,请查看perldsc

use strict;
use warnings;

my @array = (2,2,2,2,2,9,3,3,3,3);

my @grouped;
for (@array) {
    if (! @grouped || $grouped[-1][0] != $_) {
        push @grouped, [];
    }
    push @{$grouped[-1]}, $_;
}

use Data::Dump;
dd @grouped;

输出:

([2, 2, 2, 2, 2], [9], [3, 3, 3, 3])

答案 2 :(得分:3)

use List::Util 'reduce';

my @arr = (2,2,2,2,2,9,3,3,3,3);
my $result = reduce {
    if ( @$a && $b == $a->[-1][0] ) {
        push @{ $a->[-1] }, $b
    }
    else {
        push @$a, [ $b ]
    }
    $a
} [], @arr;

更简单但可能更容易阅读:

my $result = reduce {
    push @{ $a->[ @$a && $b == $a->[-1][0] ? -1 : @$a ] }, $b;
    $a
} [], @arr;

答案 3 :(得分:2)

my @arr = (2,2,2,2,2,9,3,3,3,3);

my %h;
my @newarr = map {
  my $ok = !$h{$_};
  push @{$h{$_}}, $_;

  $ok ? $h{$_} : (); 
}
@arr;

use Data::Dumper; print Dumper \@newarr;

my @arr = (2,2,2,2,2,9,3,3,3,3);

my %h;
my @newarr;
for my $v (@arr) {
  if (!$h{$v}) {
    push @newarr, ($h{$v} = []);
  }
  push @{$h{$v}}, $v;
}

输出

$VAR1 = [
      [
        2,
        2,
        2,
        2,
        2
      ],
      [
        9
      ],
      [
        3,
        3,
        3,
        3
      ]
    ];

答案 4 :(得分:2)

这将按照你的要求行事。它的工作原理是将数据内容打包为一组数字和计数,然后以所需格式解压缩。输出数据位于@num。我只使用Data::Dump来显示结果数据结构。

use strict;
use warnings;

my @arr = (2,2,2,2,2,9,3,3,3,3);

my (%rep, @num);
$rep{$_}++ or push @num, $_ for @arr;
@num = map [ ($_) x $rep{$_} ], @num;

use Data::Dump;
dd \@num;

<强>输出

[[2, 2, 2, 2, 2], [9], [3, 3, 3, 3]]

<强>更新

上述解决方案将具有相同值的所有元素收集到一个组中,即使它们来自不同的序列。如果您需要在每次更改值时拆分输出数组,那么这将满足您的需要。

use strict;
use warnings;

my @arr = (2,2,2,2,2,9,9,9,2,2,2,9,9,9);

my @groups;
for (@arr) {
  push @groups, [ ] unless @groups and $_ == $groups[-1][-1];
  push @{ $groups[-1] }, $_;
}

use Data::Dump;
dd \@groups;

<强>输出

[[2, 2, 2, 2, 2], [9, 9, 9], [2, 2, 2], [9, 9, 9]]

更新2

根据您对ikegami评论的回答,这是另一个版本,该评论显示值列表及其相关计数可能更接近您的需求。

use strict;
use warnings;

my @arr = (2,2,2,2,2,9,9,9,2,2,2,9,9,9);

my @groups;
for (@arr) {
  if (@groups and $_ == $groups[-1][0]) {
    $groups[-1][1] += 1;
  }
  else {
    push @groups, [ $_, 1 ];
  }
}

use Data::Dump;
dd \@groups;

<强>输出

[[2, 5], [9, 3], [2, 3], [9, 3]]

答案 5 :(得分:2)

强制性正则表达式回答:

my @result = map [ (ord) x length ], grep --$|, join( '', map chr, @arr ) =~ /((.)\2*)/sg;

(在no warnings "non_unicode";下)。

答案 6 :(得分:1)

您可以创建一个数组哈希,其中has的键将是一个数字。并且每次遇到该数字时,您都可以将其推送到散列的数组引用。因此,所有数字将按预期分成数组。然后,您可以遍历哈希来打印数组或按数字访问每个数组。

use strict;
use Data::Dumper;

my @arr = (2,2,2,2,2,9,3,3,3,3);
my %hash;
push(@{$hash{$_}},$_) foreach (@arr);


print Dumper(\%hash);

输出

$VAR1 = {
          '3' => [
                   3,
                   3,
                   3,
                   3
                 ],
          '9' => [
                   9
                 ],
          '2' => [
                   2,
                   2,
                   2,
                   2,
                   2
                 ]
        };