【问题标题】:How can I group sequential numbers in a perl array?如何在 perl 数组中对序列号进行分组?
【发布时间】:2015-08-03 23:16:01
【问题描述】:

我正在尝试编写一个 perl 脚本,如果我有一个如下所示的示例数组:

@array = (0,1,2,3,4,8,9,10,12,14,16,17,19)

然后我需要转换/格式化这个数组,以便我得到如下格式的输出:

output_bus[0-4, 8-10, 12, 14, 16-17, 19]

【问题讨论】:

  • 指定哪些数字应该分组,哪些不应该分组的标准是什么?
  • @Stevieb ,因此所有连续的数字都需要以分组格式呈现。例如:0,1,2,3,4 是连续的,因此它们表示为 0-4。

标签: arrays perl


【解决方案1】:
use Number::Range;
my $range = Number::Range->new();
$range->addrange(0,1,2,3,4,8,9,10,12,14,16,17,19);
my $range_list = join ', ', map join('-',@$_), $range->rangeList;

(rangeList 返回例如[0,4],[8,10],[12],[14],[16,17],[19])

还有 Set::IntSpan,它或多或少做同样的事情,只是具有重载、无限范围和其他附加功能。它这样做的方式有点麻烦:

use Set::IntSpan 'grep_spans';

my @array = (0, 1, 2, 3, 4, 8, 9, 10, 12, 14, 16, 17, 19);

my $set = Set::IntSpan->new(@array);
my @range_list;
grep_spans {
    push @range_list, $_->[0] == $_->[1] ? $_->[0] : "$_->[0]-$_->[1]";
} $set;
my $range_list = join ', ', @range_list;

这是另一种方式:

my $range_list = List::Util::reduce { $a =~ /^\z/ ? "$b" : $a =~ /-(@{[$b-1]})\z/ ? ($a =~ s/$1\z/$b/, $a) : $a =~ /(?<![0-9])@{[$b-1]}\z/ ? "$a-$b" : "$a, $b" } '', 0,1,2,3,4,8,9,10,12,14,16,17,19;

【讨论】:

  • 谢谢。您能否介绍一下 Number::Range 所属的包?由于缺少某些软件包,我无法使用上述信息!?
  • 您使用的是什么操作系统/发行版?
  • 哪个发行版? “包”在不同的 Linux 发行版中意味着非常不同的东西
  • 添加了 Set::IntSpan 示例
【解决方案2】:

巧合的是,作为我对this question 的回答的一部分,我编写了一个子程序来执行此操作。这里再次使用您自己的数据作为输入。代码假设传递的参数是按数字顺序排序的

use strict;
use warnings;

my @array = (0, 1, 2, 3, 4, 8, 9, 10, 12, 14, 16, 17, 19);

printf "output_bus[%s]\n", ranges( @array );

sub ranges {

    my @ranges;
    my ($start, $end);

    for my $n ( @_ ) {
        if ( not defined $start ) {
            $start = $end = $n;
        }
        elsif ( $n == $end + 1 ) {
            $end = $n;
        }
        else {
            push @ranges, $start == $end ? $start : "$start-$end";
            $start = $end = $n;
        }
    }

    push @ranges, $start == $end ? $start : "$start-$end" if defined $start;
    join ', ', @ranges;
}

输出

output_bus[0-4, 8-10, 12, 14, 16-17, 19]

或者,正如 DavidO 在他对该问题的解决方案中所建议的那样,Set::IntSpan 模块可以为您完成此任务,而无需考虑

这是使用该模块的解决方案的样子。唯一的不同是逗号后面没有空格,但可以很简单地修复

use strict;
use warnings;

use Set::IntSpan;

my @array = (0, 1, 2, 3, 4, 8, 9, 10, 12, 14, 16, 17, 19);

printf "output_bus[%s]\n", Set::IntSpan->new(@array);

输出

output_bus[0-4,8-10,12,14,16-17,19]

如果有人来到这里希望发现如何迭代 Set::IntSpan 对象,下面的代码可以正常工作。该文档讨论了方法startfirstnextcurrentprevlast,但没有很好地描述它们。 start 是一个重置,而next 将传递集合的下一个元素,直到它返回undef时到达末尾

这个 C 风格的 for 循环遍历所有集合

for ( my $n = $set->first; defined $n; $n = $set->next ) {
    say $n;
}

【讨论】:

  • 酷!非常感谢:)
  • @RaviRaj:不客气。另请查看我使用Set::IntSpan 模块的替代解决方案
  • Set::IntSpan 不包括请求的空格,也没有提供任何简单的方法来迭代范围(或者如果有,它会丢失在其文档的森林中)
  • @ysth:是的,我评论了逗号后缺少的空格。至于迭代集合,它很可能在那里,但我也找不到。我同意 POD 很差
  • @ysth:该模块确实支持迭代;事实上,对于这样的应用程序来说,它做得太多了。我已添加到我的帖子中
猜你喜欢
  • 2015-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-01
  • 2011-05-08
  • 2017-08-16
  • 2011-03-17
  • 1970-01-01
相关资源
最近更新 更多