Question

In research of this question I peeked at the Iterators chapter in the book Higher Order Perl, and some of the material there was a bit over my head and didn't think necessarily addressed what I specifically want here.

What I mean by lazy hashed iterator is a way to create a structure that would emulate this behavior:

%Ds = {
       '1' => 1 .. 20;
       '2' => 21 .. 40;
       '3' => 41 .. 60;
       '4' => 61 .. 80;
       ...
     }

Unfortunately, since this is a hash it would not be in order and thus useless in case of very large numbers.

The behavior is this:

I have a number. 
I need to compare it with a sequence of ranges and as a result of the comparison the 
code/sub would return another number that is the "key" of that range in case the
number is in that range. (>= with the beginning or <= with the end point of said range)
The "key" of the ranges are numbers from 1..2..3 and so on.
The code/sub will always return for a positive integer no matter how large it is.

By implementing this all lazily I mean if there is a way to emulate this behavior and not compute the sequences of ranges with their respective "keys" with every call of the sub or iteration of a loop. Basically compute once.

Yes it's true that I could choose a maximum boundary, hardcode this in a loop and be done with it but the problem is I don't know of how many of these steps I would need in the end.

Is there a way to do this with perl constructs or maybe perhaps there is a CPAN module that offers this kind of behaviour and my simple search of it didn't uncover it.

Here is a piece of code that illustrates what I mean:

sub get_nr {
  my $nr = shift;
  my %ds = map {  $a = '1' if /1/ .. /20/;
                  $a = '2' if /21/ .. /40/;
                  $a = '3' if /41/ .. /60/;
                  $a = '4' if /61/ .. /80/;
                  $_ => $a } 1 .. 80;

  while (my ($k, $v) = each %ds) {
     if ( $k == $nr){
     print "number is in range $v \n";
     }
  }
}

The output for:

 get_nr(4);
 get_nr(15);
 get_nr(22);
 get_nr(45);

Is:

number is in range 1
number is in range 1
number is in range 2
number is in range 3
Was it helpful?

Solution

Based on the discussion in the comments, the code you seem to want is a very simple subroutine

sub get_nr {
    my $nr = shift;
    my $range = int(($nr-1) / 20) + 1;
    return $range;
}

You need to compensate for the edge cases, you wanted 20 to return 1, for example, so we need to subtract 1 from the number before dividing it.

If you want to customize further, you might use a variable for the range size, instead of a hard coded number.

OTHER TIPS

sub get_range_number {
   my ($n) = @_;
   return int(($n-1)/20) + 1;
}

print "$_ is in range ".get_range_number($_)."\n"
   for 4, 15, 22, 45;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top