在Perl中,如果我有一个排序的整数数组,是否有一种简洁的方法将其转换为整数的范围的列表或数组?
E.g。假设我有:
my @numbers=(3,4,5,6,9,10,12,14,15,16,17);
我想确定存在的数字范围是:
3-6,9-10,12,14-17
我知道我可以通过For循环检查来查看我们是否已经达到了两个数组元素之间的差距等等。但在我这样做之前,我想我会看看是否有一些紧凑的符号或核心功能,可以实现这一目标。
我不想加载任何非核心库。我使用的是Cygwin Perl 5.22。
感谢。
答案 0 :(得分:6)
my @ranges;
for (@numbers) {
if (@ranges && $_ == $ranges[-1][1]+1) {
++$ranges[-1][1];
} else {
push @ranges, [ $_, $_ ];
}
}
say join ',', map { $_->[0] == $_->[1] ? $_->[0] : "$_->[0]-$_->[1]" } @ranges;
答案 1 :(得分:1)
#!/usr/bin/env perl
use strict;
use warnings;
use autouse 'YAML::XS' => 'Dump';
use Const::Fast;
use Graph::Undirected;
use List::Util qw( min max shuffle );
use Test::More;
const my %I => (in => 0, out => 1);
my @cases = (
[[shuffle 3 .. 6, 9 .. 12, 14 .. 17] => [[3, 6], [9, 12], [14, 17]]],
[[shuffle 3 .. 6, 9 .. 12, 14 .. 17, 21] => [[3, 6], [9, 12], [14, 17], [21]]],
);
for my $case ( @cases ) {
is_deeply(
spans($case->[$I{in}]),
$case->[$I{out}],
Dump($case->[$I{in}]) . ' = ' . Dump($case->[$I{out}])
);
}
done_testing;
sub spans {
my $sequence = shift;
my $g = Graph::Undirected->new;
$g->add_vertex($_) for @$sequence;
$g->has_vertex($_ + 1) and $g->add_edge($_, $_ + 1) for @$sequence;
return [
sort { $a->[0] <=> $b->[0] }
map $_->[0] == $_->[1] ? [ $_->[0] ] : $_,
map [min(@$_), max(@$_)],
$g->connected_components
];
}
输出:
$ prove -v spans.pl
ok 1 - ---
# - 9
# - 4
# - 10
# - 14
# - 6
# - 5
# - 3
# - 15
# - 12
# - 17
# - 11
# - 16
# = ---
# - - 3
# - 6
# - - 9
# - 12
# - - 14
# - 17
#
ok 2 - ---
# - 17
# - 16
# - 12
# - 11
# - 6
# - 9
# - 10
# - 5
# - 3
# - 4
# - 21
# - 14
# - 15
# = ---
# - - 3
# - 6
# - - 9
# - 12
# - - 14
# - 17
# - - 21
#
1..2
ok
All tests successful.
Files=1, Tests=2, 1 wallclock secs ( 0.04 usr 0.01 sys + 0.25 cusr 0.02 csys = 0.32 CPU)
Result: PASS
答案 2 :(得分:-1)
好的,在搞乱了几分钟之后,以下似乎在我试过的几个快速测试集上运行得相当好。如果有人对这种方法有任何想法,我会很感激。
use strict;
use warnings;
my @numbers=(-7,-3,-2,-1,3,4,5,6,9,10,12,14,15,16,17);
my @ranges=();
my $start;
$start=$numbers[0];
for(my $i=0; $i<@numbers; $i++){
if($numbers[$i]>$numbers[$i-1]+1){
push @ranges, ($start == $numbers[$i-1] ? $start : $start.":".$numbers[$i-1]);
$start=$numbers[$i];
print "New start: ".$numbers[$i]."\n";
}
}
push @ranges, ($start == $numbers[@numbers-1] ? $start : $start.":".$numbers[@numbers-1]);
print join(",",@ranges)."\n";