【问题标题】:split numeric array at specific positions在特定位置拆分数值数组
【发布时间】:2014-08-11 11:48:30
【问题描述】:

我正在尝试将一个数值数组拆分为较小的数组,这样每个较小的数组都不能包含任何不同的数字。

示例:数组(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,但想不出一个好的解决方案。

【问题讨论】:

  • [[2,5],[9,1],[3,4]] 会不会比 [[2,2,2,2,2],[9],[3,3,3,3]] 更有用?
  • 当然,这也可以。虽然我可以使用单个数组的长度,所以没关系。
  • 哇,我正在投票赞成这个问题并盯着它看 - 我很想将标题编辑成更壮观的东西:谁知道?感谢大家的教育。

标签: arrays perl reference split


【解决方案1】:

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;
  } @_;
}

【讨论】:

  • my %rep; my @part = part { $rep{$_} //= keys(%rep) - 1; } @array;
  • @Borodin:你假设数字以后不会重复
  • @ysth:我假设,如果数字稍后重复,它们应该与其他具有相同值的值出现在同一组中。你假设他们不应该。我们可以判断谁是对的。
  • @ysth 我添加了一个对多次调用不敏感的版本,以防您的观察是预期用途的问题。
  • @ikegami:我确信ysth 很高兴并且能够在不需要你帮助的情况下不同意。我也确信他意识到我的只是嘲讽,这个世界现在不需要拯救。顺便说一句,我最后一句话的意思是“我们无法分辨谁是对的!”
【解决方案2】:

创建一个由相似元素分组的数组。

如需复习复杂数据结构,请查看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])

【讨论】:

    【解决方案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;
    

    【讨论】:

    • 酷......有很多方法可以做到这一点。如果 perl5 曾经得到一个 comb 类型的函数(它会在 Scalar::Util 中去哪里?)我想它会非常非常好,因为 TIMTOWTDI。
    【解决方案4】:
    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
          ]
        ];
    

    【讨论】:

      【解决方案5】:

      强制正则表达式答案:

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

      (在no warnings "non_unicode"; 下)。

      【讨论】:

      • 嘿很好——使用 perl 锤子,事情总是看起来像正则表达式。 “我正则表达式一次,你可耻 - 正则表达式我 1000 次,我可耻”;-)(含糊地引用 masak)
      【解决方案6】:

      这将满足您的要求。它的工作原理是将数据内容打包为一组数字和计数,然后以所需的格式解包。输出数据在@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]]
      

      【讨论】:

      • 如果数字稍后重复,则可能存在缺陷。
      • @Miller:根据在这种情况下所需的行为,可能是您自己的解决方案存在缺陷!
      • 这给了我一个 perl 5.16.2 的语法错误,near "} for "
      • @user1981275:你可能有一个旧版本。再复制一份。
      • +1 A+ 表示解释的彻底性、各种解决方案、散文、编辑和更正,以及对细节的关注。对于一个相对微不足道的问题,这一切都进一步证明了您的贡献的价值。谢谢!
      【解决方案7】:

      您可以创建一个数组哈希,其中 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
                       ]
              };
      

      【讨论】:

        猜你喜欢
        • 2021-07-07
        • 2019-11-08
        • 1970-01-01
        • 2021-05-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多