tags:

views:

92

answers:

3

Hello,

I would like to use a string (e.g. read from a file) as a piece of code in my perl program. For example, I read a string (e.g. $str="1..100,171,398..1000") then I would like to print all the numbers in the range represented by the string.

Thanks, Dave

+3  A: 

For that specific case, see http://stackoverflow.com/questions/1016766/how-can-i-expand-a-string-like-1-15-16-into-a-list-of-numbers

In general, eval is used to execute code in a string. If you have an entire file of code to execute, use do. But make sure that the file/string are secure before considering this. If in fact the "code" is restricted to something like your example, do not use eval or do.

Update: turns out Number::Range is dog slow for large ranges; for your case you are far better off using - instead of .. in your ranges and using Set::IntSpan. You ask about efficiency: using an array will consume a few megabytes of memory, but that isn't all that bad. Set::IntSpan does provide iteration methods you could use instead of just generating the array, but I probably wouldn't bother.

ysth
Thanks for Number::Range! Adding another question: my ranges are quite large (~500k numbers, usually in two blocks only, i.e. something like 1..20000,,700000..1000000). What is the most efficient way to simply print them out to a file? Creating an actual array (as in the example) does not seem like a great idea, or does it?
David B
A: 

You can use eval to execute code in a variable. Although the interpolation can be fun.

eval "for ($str) { print \"\$_\n\";}"

10rd_n3r0
+1  A: 

Number::Range uses the .. syntax that you have, but it doesn't have an iterator. Set::IntSpan uses - instead of .., but it does have an iterator, which makes printing out the set without creating a giant array easy.

use Set::IntSpan;

my $str = "1..100,171,398..1000";
$str =~ s/\.\./-/g;
my $set = Set::IntSpan->new($str);

for (my $i = $set->first; defined $i; $i = $set->next) {
  print "$i\n"; # Or however you'd like to format it
}

Internally, Set::IntSpan stores the ranges, so this should be fairly memory-efficient. You could also use the spans method to get the parsed ranges. This would require you to write a bit more code, but would mean you don't have to do a method call for each number in the range. Method calls in Perl are a bit slow, but I wouldn't worry about it unless the above code takes too long to run.

cjm
Thank you cjm. An nice and elegant solution, but it doesn't work for me since Set::IntSpan requires the ranges to be sorted, while mine aren't necessarily (for a reason).
David B