#!/usr/bin/perl
use strict;
use warnings;

while (<DATA>) {
  my @list = split;
  my $compact = format_number_list(@list);
  $compact."\n" eq <DATA> or warn "@list => $compact\n";

  my @expanded = expand_number_list($compact);
  join("\001" => @expanded) eq join("\001" => @list)
    or warn "$compact => @expanded\n";
}

sub format_number_list {
  die "Non-integer input @_\n" if grep !/^-?\d+$/ => @_;
  my $result = join ', ' => @_;
  {
    no warnings 'uninitialized';
    $result =~ s/(?<!\d)(-?\d+), (?=(??{$1+1})(?!\d))/$1#$2/g;
  }
  $result =~ s/#([\d#-]+#)?/-/g;   # Delete interstitial
  $result;
}

sub expand_number_list {
  my @result = split /\s*,\s*/ => $_[0];
  @result == grep /^-?\d+(--?\d+)?$/ => @result
    or die "Invalid string $_[0]\n";
  # The +0 is to work around a 5.[68] bug when $2 eq "0"
  map { /(-?\d+)-(-?\d+)/ ? $1+0 .. $2+0 : $_ } @result;
}


__END__
1 2 4 5 6 7 9 13 24 25 26 27
1-2, 4-7, 9, 13, 24-27
0 17 18 99
0, 17-18, 99
27 26 25 24 13 9 7 1 2 4 6 5
27, 26, 25, 24, 13, 9, 7, 1-2, 4, 6, 5
-1 0 1 2 13 17 28
-1-2, 13, 17, 28
0 1 1 2 3 9 8 7 6 9
0-1, 1-3, 9, 8, 7, 6, 9
-4 -3 -1 0
-4--3, -1-0
3.4 -1.2 -1.4 4.4
won't get here :-) 


The ugliest part to me is the negative lookbehind/ahead assertions in format_number_list, without which the input (18, 99) is incorrectly transformed to "18-99", since the bump-along tries the 8 followed by 99, fails, backtracks to 8 followed by 9 and succeeds.  I thought I should have been able to solve that by using non-backtracking grouping (?>...), but I couldn't get it to work in a brief attempt.

If someone supplies a range to expand_number_list() where the first element is greater than the second, the output seems quite plausible (ignores that range).  Arguably it should throw an exception, though.
-- 
Peter Scott
Pacific Systems Design Technologies 
http://www.perldebugged.com/

