A sort of perl golf challenge

I've been reviewing some perl code code that is due to go back into Solaris shortly, and one of the routines takes a sorted array of integers and returns a string with contiguous ranges of numbers collapsed and other numbers comma-separated. For example, given an array containing 1, 3, 4, 5, 8, 9, 10, 12 the routine would return the string "1, 3-5, 8-10, 12". The routine iterates over the array looking for and collapsing sequences of numbers. It's 30 lines long, but always being one for taking up a pointless challenge I wondered if I could make it any shorter. Here's what I came up with:

sub collapse
{
        my $str = join(', ', @_);
        while ($str =~ s{\b(\d+), ((??{ $1 + 1 }))\b}{$1-$2}g) {}
        $str =~ s{-(?:\d+-)+}{-}g;
        return ($str);
} 

Now I know that under the proper perl golf rules I could shorten that down by removing whitespace, using implicit assignment and matches against $_ and so forth but I'm more interested in seeing if anyone can come up with a conceptually shorter solution (i.e. one that I can still read ;-). It occured to me that you might be able to do something smart with a recursive regexp, but the requirement to use $1 + 1 to spot a sequence kept stymieing me. However I'm sure some other perl saddo out there will come up with something even shorter and far smarter. Anyone? ;-)

Tags : ,
Categories : Tech, Perl


Re: A sort of perl golf challenge

I'm not a champion at Perl but here is my try.

I found that you can refer to whatever was matched by the most-recently closed group by using $^N inside the embedded Perl code.

The collapse subroutine might now look like this:

sub collapse
{
my $str= join(', ', @_);
$str=~ s{(\d+)(?:, ((??{ $^N + 1 })))+}{$1-$2}g;
return( $str);
}

I don't find it more obscure than the previous subroutine. What do you think?

Re: A sort of perl golf challenge

The Number::Range module gets close to your desired output:
use Number::Range;
my $r = new Number::Range '1,3,4,5,8,9,10,12';
print scalar($r->range), "\n";
produces:
1..1,3..5,8..10,12
(why 1..1 ??) Maybe that's a potential way to go?

Re: A sort of perl golf challenge

Naw, I get to set the rules, and I've decided using a module is cheating ;-)

Re: A sort of perl golf challenge

sub collapse {
my $str = join ', ', @_;
$str =~ s/(\d+), ((??{$^N + 1})(?:, )?)+/$1-$^N/g;
return $str;
}

Re: A sort of perl golf challenge

Ooh, that's *sneaky*. However it barfs under "use warnings":
Argument "2, " isn't numeric in addition (+) at (re_eval 1) line 1.
Can't see any easy way of fixing it though...

Re: A sort of perl golf challenge

Got it. This one’s in fact even shorter, heh.
sub collapse {
my $str = join ', ', @_;
$str =~ s/(\d+)(?:, ((??{$^N + 1})))+/$1-$^N/g;
return $str;
}
But I just noticed it’s buggy without the \b assertions your initial attempt had:
sub collapse {
my $str = join ', ', @_;
$str =~ s/\b(\d+)(?:, ((??{$^N + 1})\b))+/$1-$^N/g;
return $str;
}
PS.: it would be nice if <code> tags were permitted in comments…

Re: A sort of perl golf challenge

Re: A sort of perl golf challenge

Nice, moving the ", " match before the repeating part means you can still do the repeated capture trick. And yes, I did see your previous comment - yours was the shortest so I guess you win;-)

I don't know why code tags aren't allowed, but pre tags seem to work OK.

Re: A sort of perl golf challenge

Alan,

thats awesome I don't quite understand how this thing works could it return the data as arrays ?
somthing like this ?
"[1],[3,4,5],[8,9,10],[12]"
though for my purpose it would be better if "[]" these where "{}" these instead