in reply to [Solved] Converting a list of numbers to use a range operator

This probably belongs to the obfuscation section, but I was wondering whether there would be a s/// expression that one could apply to "2,3,4,8,10,11,12,13,14,15" repeatedly until it becomes "2-4,8,10-15"?

Any ideas?

To clarify, the expression would create a series of strings like this or similar:

"2,3,4,8,10,11,12,13,14,15" "2-4,8,10,11,12,13,14,15" "2-4,8,10-12,13,14,15" "2-4,8,10-13,14,15" "2-4,8,10-14,15" "2-4,8,10-15"

I hope this will not throw this thread of track...

Replies are listed 'Best First'.
Re^2: Converting a list of numbers to use a range operator
by AnomalousMonk (Archbishop) on Mar 24, 2013 at 00:28 UTC

    Don't know if this solution is really preferable to any of the others, but its got a  s/// in there! Uses state feature (available with 5.10+), but could easily avoid it. Note that it does not bother to range-ize a degenerate range: '1,2' does not become '1-2'.

    >perl -wMstrict -le "use feature 'state'; ;; my $s = '2,3,5,6,7,9,11,12,13,14,16,17,19'; print qq{'$s' \n}; ;; my $sep = qr{ \s* , \s* }xms; my $n = qr{ \d+ }xms; ;; sub order { state $p = 0; my $t = $p; $p = $_[0]; return $_[0] - $t == 1; } ;; use re 'eval'; $s =~ s{ (?<! \d) ($n) (?{ order($^N) }) (?= $sep ($n) (?(?{ ! order($^N) }) (*F)) (?: $sep ($n) (?(?{ ! order($^N) }) (*F)))+ ) .+? \3 (?! \d) } {$1-$3}xmsg; ;; print qq{'$s' \n}; " '2,3,5,6,7,9,11,12,13,14,16,17,19' '2,3,5-7,9,11-14,16,17,19'

    Update: Actually, the non-capturing look-ahead folderol is not needed. The following  s/// seems to work just as well.

    $s =~ s{ (?<! \d) ($n) (?{ order($^N) }) (?: $sep ($n) (?(?{ ! order($^N) }) (*F))){2,} } {$1-$2}xmsg;

    Update: Even slightly simpler and no numbered capture group variables, but  \K only available with 5.10+, like state.

    sub order { state $p = 0; my $t = $p; $p = $^N; return $^N - $t == 1; } $s =~ s{ (?<! \d) ($n) \K (?{ order() }) (?: $sep ($n) (?(?{ ! order() }) (*F))){2,} } {-$^N}xmsg;