(jeffa) Re: Expand Ranges in Lists of Numbers
by jeffa (Bishop) on Jun 08, 2003 at 20:00 UTC
|
If [your original code] had more error-checking
[and features like it does now], i wouldn't say that
you are doing too much work. Using eval here
gives pretty much the exact same results and
requires hardly any effort on the coder's behalf ... just
get rid of spaces and change all dashes to two dots:
use Data::Dumper;
print Dumper [evalexp('21, 35, 42-45, 19')];
sub evalexp {
my $str = shift;
$str =~ s/\s//g;
$str =~ s/-/../g;
return eval $str;
}
By the way, in your example, this line:
push @expanded, $piece;
pushes "strings" onto the array. You probably should
"cast" $piece with int:
push @expanded, int $piece;
instead. (The ranges don't suffer from this side effect [caused by split i believe] because of the
.. operator.)
Data::Dumper reveals such details.
Happy coding. :)
jeffa
L-LL-L--L-LL-L--L-LL-L--
-R--R-RR-R--R-RR-R--R-RR
B--B--B--B--B--B--B--B--
H---H---H---H---H---H---
(the triplet paradiddle with high-hat)
| [reply] [d/l] [select] |
|
|
Thanks, I didn't even think of doing it that way! :-) I would change
$str =~ s/\s//g;
to
$str =~ s/[^\d-,]//g;
though when handling untrusted data.
As they say, TMTOWTDI. | [reply] [d/l] [select] |
Re: Expand Ranges in Lists of Numbers
by atcroft (Abbot) on Jun 08, 2003 at 20:25 UTC
|
Very interesting, and potentially very, very useful. It might also be useful to have the reverse function, compacting a list of numbers into a list containing ranges.
The code below seems to do this, with the caveats that:
- if numbers appear twice in the list, only one instance is returned,
- the original ordering is lost, as the values in this code are sorted to ease processing, and
- the code is a sample, and someone more skilled would probably be able to do the same better or more efficiently
With that in mind, here is my contribution:
sub compactRanges {
#
# @pieces contains the list of numbers
# %parts will contain a compact form, such that $start..$end
# is represented as $parts{$start} = $end
# $seperator contains the character(s) placed between the
# values
# %wrap_negatives contains a flag indicated if negative
# values should be wrapped in some way, and the leading
# and trailing values if so
#
my (@pieces) = @_;
my $seperator = '..';
my %wrap_negatives =
( 'flag' => 1, 'leading' => '[', 'trailing' => ']' );
my (%parts);
@pieces = sort { $a <=> $b } @pieces;
{
my ($i);
my ($recent);
$recent = $parts{$i} = $i = shift (@pieces);
while (@pieces) {
$i = shift (@pieces);
$recent = $i if ( $i - 1 > $parts{$recent} );
$parts{$recent} = $i;
}
}
foreach my $k ( sort { $a <=> $b } keys(%parts) ) {
my $str =
( ( $wrap_negatives{'flag'} )
and ( $k < 0 ) ? $wrap_negatives{'leading'} : '' )
. $k
. ( ( $wrap_negatives{'flag'} )
and ( $k < 0 ) ? $wrap_negatives{'trailing'} : '' );
$str .= $seperator
. (
( $wrap_negatives{'flag'} )
and ( $parts{$k} < 0 ) ? $wrap_negatives{'leading'}
: ''
)
. $parts{$k}
. (
( $wrap_negatives{'flag'} )
and ( $parts{$k} < 0 ) ? $wrap_negatives{'trailing'}
: ''
)
if ( $parts{$k} != $k );
push ( @pieces, $str );
}
return (@pieces);
}
| [reply] [d/l] |
|
|
| [reply] |
Re: Expand Ranges in Lists of Numbers
by valdez (Monsignor) on Jun 08, 2003 at 21:49 UTC
|
| [reply] |
|
|
Thanks! I didn't know Dominus did a Perl QOTW. I guess I'll have to subscribe... ; )
| [reply] |
(jeffa) Re: Expand Ranges in Lists of Numbers
by jeffa (Bishop) on Jun 09, 2003 at 01:14 UTC
|
Hate to flog a dead horse (not that your code is a dead
horse - i just like that expression), but this:
$tmp[0] > $tmp[1]
? push @expanded, reverse ($tmp[1]..$tmp[0])
: push @expanded, ($tmp[0]..$tmp[1]);
is bad style because the ternery operator is being used
in void context. You would be better using an if-else
statement, because it is more readable. If you prefer the
brevity of the ternery operator, then please use it as an
assignment operator:
push @expanded, $tmp[0] > $tmp[1]
? reverse ($tmp[1]..$tmp[0])
: ($tmp[0]..$tmp[1])
;
I like the changes, by the way. Now we have a fully flogged
dead horse. :D
jeffa
L-LL-L--L-LL-L--L-LL-L--
-R--R-RR-R--R-RR-R--R-RR
B--B--B--B--B--B--B--B--
H---H---H---H---H---H---
(the triplet paradiddle with high-hat)
| [reply] [d/l] [select] |
|
|
(
$tmp[0] > $tmp[1]
? sub { push @expanded, reverse ($tmp[1]..$tmp[0]) }
: sub { push @expanded, ($tmp[0]..$tmp[1]) }
)->();
:->
Makeshifts last the longest. | [reply] [d/l] |
|
|
| [reply] |
|
|
print "that's the answer" if $answer == 42;
die "that's not your file!" unless -o $file;
A caveat i teach my C++ lab students (i am a graduate
teaching assistant at MTSU) is that even though braces are
optional for one line blocks, they should get in the habit
of using them always. Why? Because 9 times outta 10 you
will want to add a second line in the near future.
Murphy's law or something ... ;)
jeffa
L-LL-L--L-LL-L--L-LL-L--
-R--R-RR-R--R-RR-R--R-RR
B--B--B--B--B--B--B--B--
H---H---H---H---H---H---
(the triplet paradiddle with high-hat)
| [reply] [d/l] |
|
|
|
|
|
Re: Expand Ranges in Lists of Numbers
by svsingh (Priest) on Jun 08, 2003 at 15:46 UTC
|
Nice. This can come in handy. My only suggestion (minor) would be to sort the list before you return it. Unless there's a reason you left that out or know that your input will always be in order. Thanks. | [reply] |
|
|
Well, I'd prefer to leave the array in the order given (just in case the user wants it that way), but if you wanted it sorted you could do one of two things:
- Replace the line return @expanded; with return sort @expanded;
- When calling it, just sort the results. Ex/ my @results = sort expandRange($string);
| [reply] [d/l] [select] |
Re: Expand Ranges in Lists of Numbers
by zentara (Cardinal) on Jun 09, 2003 at 20:39 UTC
|
Heres another snippet for consideration.
#!/usr/bin/perl
use warnings;
my $ranges = '1,3-7,9-22';
print "$ranges\n";
$ranges =~ s/-/../g;
my @array = map {$_} eval $ranges;
print "@array\n";
| [reply] [d/l] |
Re: Expand Ranges in Lists of Numbers
by belg4mit (Prior) on Jun 09, 2003 at 15:39 UTC
|
| [reply] |