dragonchild has asked for the wisdom of the Perl Monks concerning the following question:

I've got the following code. The idea is to convert an array representing a bitmap to a string representing the range of that bitmap. So, (1,1,0,1,1,1,0) would convert to "0-1,3-5". (In my code, I'm converting from 0-base to 1-base using the map.)
my @channels = (1, 1, 0, 1, 1, 1, 0); my $val = join ',', map { $_ + 1 } grep { $channels[$_] } 0 .. $#chann +els; $val =~ s/(\d+),(\d+)/$1+1 == $2 ? "$1-$2" : "$1,$2"/eg; print $val, $/;
Unfortunately, the regex only works if my set-bits are only in pairs. If I put the triple 1,1,1 (as above), it only works for the first pair. In addition, (1,1,1,0,1,1,0) converts to "1-2,3,5,6", which totally boggles me.

Help?

------
We are the carpenters and bricklayers of the Information Age.

Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

Replies are listed 'Best First'.
(Ovid) Re: Question on s///eg
by Ovid (Cardinal) on Feb 27, 2002 at 17:19 UTC

    Assuming you're using 5.6.0 or greater, you can use a range generator that japhy put together: List-to-Range generation. The rest should just be details :)

    my @channels = (1, 1, 0, 1, 1, 1, 0); my @vals = map { $_ + 1 } grep { $channels[$_] } 0 .. $#channels; my $val = num2range( @vals ); print $val, $/; sub num2range { local $_ = join ',' => sort { $a <=> $b } @_; s/(?<!\d)(\d+)(?:,((??{$++1})))+(?!\d)/$1-$+/g; return $_; }

    Cheers,
    Ovid

    Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

Re: Question on s///eg
by japhy (Canon) on Feb 27, 2002 at 17:46 UTC
    Well, since I love regexes, I'll help out...
    $bitmap =~ s{ (1 (?: ,1 )* ) # match 1 or 1,1 or 1,1,1,... | ,? (0 (?: ,0 )* ) # match 0 or 0,0 or 0,0,0,... }{ # evil tr/// bug with $<DIGITS> my ($one, $zero) = ($1, $2); ++$i; if ($zero) { $i += $zero =~ tr/,//; ""; } else { if (my $c = $one =~ tr/,//) { "$i-" . ($i += $c) } else { $i } } }xge;

    _____________________________________________________
    Jeff[japhy]Pinyan: Perl, regex, and perl hacker, who'd like a (from-home) job
    s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;

Re: Question on s///eg
by particle (Vicar) on Feb 27, 2002 at 17:25 UTC
    by the way, for long lists, you should probably be using vec. it will save you *a lot* of memory.

    ~Particle ;Þ

Re: Question on s///eg
by PrakashK (Pilgrim) on Feb 27, 2002 at 17:27 UTC
    Here's some not so thouroughly tested code:
    my @channels = (1, 1, 0, 1, 1, 1, 0); my $str = join "", @channels; my @range; push @range, join "-", ($-[0], $+[0]-1) while $str =~ m/(1+)/g; print join ",", @range;
    prints 0-1,3-5

    /prakash

    Update: I must add that this may not work for perl versions older than 5.6.0.

Re: Question on s///eg
by zengargoyle (Deacon) on Feb 27, 2002 at 22:59 UTC

    I've forgotten where this came from, but it could be helpful.

    $n="1,2,3,4,9,10,11,12,14,16,17,18"; $n =~ s{ (?<!\d) (\d+) (?: , ( (??{ 1 + $+ }) ) )+ }{$1-$+}gx; print "$n\n"; $ perl cool_regex.pl 1-4,9-12,14,16-18
      That's mine, from Snippets. But you can feel free to use it!

      _____________________________________________________
      Jeff[japhy]Pinyan: Perl, regex, and perl hacker, who'd like a (from-home) job
      s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;

        In my head, almost all my regex's are from japhy.

        moment of silence...

Re: Question on s///eg
by Sweeper (Pilgrim) on Feb 28, 2002 at 06:51 UTC
    Maybe you could take a look at Mastering Algorithms in Perl page 228, or install Steven McDougall's module Set::IntSpan. (I have done the former, maybe I will try the latter sometime).