perl - How to extend a binary search iterator to consume multiple targets -
i have function, binary_range_search, called so:
my $brs_iterator = binary_range_search(     target => $range,                   # eg. [1, 200]     search => $ranges                   # eg. [ {start => 1,   end => 1000}, );                                      #       {start => 500, end => 1500} ]   brs_iterator->() iterate on @$ranges on $range overlaps.
i extend binary_range_search able call multiple ranges target, eg:
target => $target_ranges # eg. [ [1, 200], [50, 300], ... ] search => $search_ranges # above   so, when search on $range->[0] exhausted, should move on $range->[1], , on. here function in question, in original form:
sub binary_range_search {     %options = @_;     $range    = $options{target}  || return;     $ranges   = $options{search}  || return;      ( $low, $high ) = ( 0, @{$ranges} - 1 );      while ( $low <= $high ) {          $try = int( ( $low + $high ) / 2 );          $low  = $try + 1, next if $ranges->[$try]{end}   < $range->[0];         $high = $try - 1, next if $ranges->[$try]{start} > $range->[1];          ( $down, $up ) = ($try) x 2;          %seen = ();          $brs_iterator = sub {              if (    $ranges->[ $up + 1 ]{end}       >= $range->[0]                     , $ranges->[ $up + 1 ]{start} <= $range->[1]                     , !exists $seen{ $up + 1 } )             {                 $seen{ $up + 1 } = undef;                 return $ranges->[ ++$up ];             }             elsif ( $ranges->[ $down - 1 ]{end}       >= $range->[0]                     , $ranges->[ $down + 1 ]{start} <= $range->[1]                     , !exists $seen{ $down - 1 }                     , $down > 0 )             {                 $seen{ $down - 1 } = undef;                 return $ranges->[ --$down ];             }             elsif ( !exists $seen{$try} ) {                 $seen{$try} = undef;               return $ranges->[$try];             }             else {                 return;             }          };         return $brs_iterator;     }     return sub { }; }   it's standard binary search strategy, until finds overlapping range. moves on right, exhausts it, moves on left, exhausts it, , gives up. ideally, should maybe shift next target range, , redo search, suppose (perhaps via recursion?). problem is, not sure how make work iterator construction.
i wrapped iterator generation in loop, , built array of iterator functions.
depending on context, either return master iterator or list of iterator functions. wasn't sure wanted.
use strict; use warnings;   $t = [ [1,200], [400,900] ]; @r = (     { start =>   1, end =>  100 },     { start =>   2, end =>  500 },     { start => 204, end =>  500 },     { start => 208, end =>  500 },     { start => 215, end => 1000 },     { start => 150, end => 1000 },     { start => 500, end => 1100 }, );  # master iterator process each iterator in turn. $brs_iterator = binary_range_search(     targets => $t,       search => \@r, );  # array of iterators @brs_iterator = binary_range_search(     targets => $t,       search => \@r, );    sub binary_range_search {     %options = @_;     $targets = $options{targets}  || return;     $ranges  = $options{search}  || return;       @iterators;      target:     $target ( @$targets ) {          ( $low, $high ) = ( 0, $#{$ranges} );          range_check:         while ( $low <= $high ) {              $try = int( ( $low + $high ) / 2 );              # remove non-overlapping ranges             $low  = $try + 1, next range_check                  if $ranges->[$try]{end}   < $target->[0];              $high = $try - 1, next range_check                  if $ranges->[$try]{start} > $target->[1];              ( $down, $up ) = ($try) x 2;              %seen = ();              $brs_iterator = sub {                  if (    exists $ranges->[$up + 1]                         , $ranges->[ $up + 1 ]{end}   >= $target->[0]                         , $ranges->[ $up + 1 ]{start} <= $target->[1]                         , !exists $seen{ $up + 1 } )                 {                     $seen{ $up + 1 } = undef;                     return $ranges->[ ++$up ];                 }                 elsif ( $ranges->[ $down - 1 ]{end}       >= $target->[0]                         , $ranges->[ $down + 1 ]{start} <= $target->[1]                         , !exists $seen{ $down - 1 }                         , $down > 0 )                 {                     $seen{ $down - 1 } = undef;                     return $ranges->[ --$down ];                 }                 elsif ( !exists $seen{$try} ) {                     $seen{$try} = undef;                   return $ranges->[$try];                 }                 else {                     return;                 }              };             push @iterators, $brs_iterator;             next target;         }      }      # in scalar context return master iterator iterates on list of range iterators.     # in list context returns list of range iterators.     return wantarray           ? @iterators           : sub {               while( @iterators ) {                  if( $range = $iterators[0]() ) {                      return $range;                  }                  shift @iterators;              }              return;         };  }      
Comments
Post a Comment