pythonregexrangeslice

Python equivalent of the Perl ".." flip-flop operator


What is the Python equivalent of the Perl ".." (range, or flip-flop) operator?

for ( qw( foo bar barbar baz bazbaz bletch ) ) { 
    print "$_\n" if /ar.a/ .. /az\w/;
}

Output:

barbar
baz
bazbaz

The Python workaround that I am aware of includes generator expression and indexing with the help of enumerate, but this seems cumbersome:

import re

lst = 'foo bar barbar baz bazbaz bletch'.split()
idx_from = list(i for i, el in enumerate(lst) if re.search(r'ar.a', el))[0]
idx_to   = list(i for i, el in enumerate(lst) if re.search(r'az\w', el))[0]

lst_subset = lst[ idx_from : (idx_to+1)]
print(lst_subset)
# ['barbar', 'baz', 'bazbaz']

Note:

I am looking for just one range. There is currently no need to have multiple ranges.


Solution

  • When the operands aren't simple numbers, EXPR1 .. EXPR2 in scalar context is equivalent to following (except for the scope created by do { }):

    do {
       state $hidden_state = 0;
       if ( $hidden_state ) {
          ++$hidden_state;
       } else {
          $hidden_state = 1 if EXPR1;
       }
    
       my $rv = $hidden_state;
    
       # Or `$hidden_state > 1 && EXPR2` for `...`.
       if ( $hidden_state && EXPR2 ) {
          $rv .= "E0";
          $hidden_state = 0;
       }
    
       $rv
    }
    

    Since you only care whether the flip-flop returns true or false, the above simplifies to the following:

    do {
       state $hidden_state = false;
       $hidden_state ||= EXPR1;
       my $rv = $hidden_state;
       $hidden_state &&= EXPR2;
       $rv
    }
    

    Now we have to translate this. Since the flip-flip is usually used as a generator, that's what I'll create.

    def flipflop( enumerable, start_cond, end_cond ):
       state = False
       for val in enumerable:
          if not state:
             state = start_cond( val )
          if state:
             yield val
          if state:
             state = end_cond( val )
    
    import re
    
    lst = 'foo bar barbar baz bazbaz bletch'.split()
    
    for x in flipflop( lst, lambda v: re.search( r'ar.a', v ), lambda v: re.search( r'az\w', v ) ):
       print( x )