我正在尝试编写一个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]
答案 0 :(得分:6)
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;
答案 1 :(得分:3)
巧合的是,我写了一个子程序来完成这个,作为我对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
对象,下面的代码工作正常。该文档讨论了方法start
,first
,next
,current
,prev
和last
,但没有很好地描述它们。 start
是重置,而next
将传递集合的下一个元素,直到它返回到undef
时结束
这个C风格的for
循环遍历所有集合
for ( my $n = $set->first; defined $n; $n = $set->next ) {
say $n;
}