eXile has asked for the wisdom of the Perl Monks concerning the following question:

Hi, Whenever I use the .. operator to do some matching of records in a file I constantly bump into the fact that the condition that causes the right operand to become false again evaluates to true. Example:
my @aap = qw( 0aap 0noot 1mies 2mies 0aap 0noot 1mies 6mies ); foreach (@aap) { if (/^1(.*)/ .. /^0(.*)/) { print "$1\n"; } } __END__ mies mies aap mies mies
I don't want to process the line containing '0aap' (because it ended the .. operator), so I do something like:
#!/usr/bin/perl my @aap = qw( 0aap 0noot 1mies 2mies 0aap 0noot 1mies 6mies ); foreach (@aap) { if (( /^1(.*)/ .. /^0(.*)/ ) && !/^0/) { print "$1\n"; } } __END__ mies mies mies mies
which works but is harder to read, and less elegant. Do other people use more elegant/readable solutions for this?

Replies are listed 'Best First'.
Re: .. operator and not including the condition of right operand
by bart (Canon) on Apr 27, 2005 at 19:17 UTC
    Inspect the result of the range operator a little closer... you'll find that it's not only usable as a boolean, but as a counter, and as an end indicator too. When the right hand evaluates to true, the value will contain an "E" (from exponent).

    This variation:

    my @aap = qw( 0aap 0noot 1mies 2mies 0aap 0noot 1mies 6mies ); foreach (@aap) { if (my $test = ( /^1(.*)/ .. /^0(.*)/ )) { print "$test: $1\n"; } }

    Prints:

    1: mies
    2: mies
    3E0: aap
    1: mies
    2: mies
    
    Spot the "E0"?

    Alternatively, you can set a flag in the right hand side.

    my @aap = qw( 0aap 0noot 1mies 2mies 0aap 0noot 1mies 6mies ); foreach (@aap) { if (/^1(.*)/ .. (my $last = /^0(.*)/ )) { unless($last) { print "$1\n"; } } }
    Result:
    mies
    mies
    mies
    mies
    

      In the "perverse" category:

      use strict; use warnings; my @aap = qw( 0aap 0noot 1mies 2mies 0aap 0noot 1mies 6mies ); for (@aap) { print "$_\n" if - reverse '0' . (/^1(.*)/ .. /^0(.*)/); } __END__ 1mies 2mies 1mies 6mies
      I wonder what's more common, the situation described by the OP or the need to know the sequence number of the match? If the former is more common, then I think one could make the case for having the match of the right operand return a string of the form '0En' instead of the current 'nE0'. Then, all that would be required to satisfy the OP's request would be to prepend 0+, or better yet, a minus sign, in front of the .. expression:
      if ( -(/^1(.*)/../^0(.*)/) )
      whereas if one wanted to keep the line matching the right operand one would omit the 0+ or minus sign:
      if ( (/^1(.*)/../^0(.*)/) )
      Those who actually wanted the match number, would have to scrape it off the return value:
      if ( ( /^1(.*)/../^0(.*)/ ) =~ /(\d+)$/ ) { # $1 contains the sequence number of match
      I've tested this scheme a bit, and it seems to work; please let me know if I missed some corner (or not-so-corner) cases.

      the lowliest monk

        The reason for appending "E0" is that the value in numerical context is unchanged: "3E0" as an integer is 3 (just like "3E1" is 30; not because numification ignores everything from the "E" till the end). Your value would means zero.

        And I don't think 0+$count is easier than $count =~ /E/. Nor faster, or much faster at least. Your /(\d+)$/ definitely isn't easy, nor likely very fast.

        As you can guess from the generally unknown behaviour of what .. actually returns, neither requirements are very common. That's why I'm most happy with the status, like it is.

Re: .. operator and not including the condition of right operand
by dragonchild (Archbishop) on Apr 27, 2005 at 18:53 UTC
    What should you do for qw( 0aap 2mies 0aap 1mies 2mies 0aap );? That will determine the best answer.

    The Perfect is the Enemy of the Good.

      1mies should start the match, so
      mies mies
      should be printed in that case (updated the OP, to clarify)
        my $good; for (@aap) { my ($type, $value) = /^(\d)(.*)/ or next; if ( $type == 0 ) { $good = 0; next; } elsif ($type == 1) { $good = 1; } next unless $good; print "$value\n"; }
        (Untested)

        The Perfect is the Enemy of the Good.

Re: .. operator and not including the condition of right operand
by Fletch (Bishop) on Apr 27, 2005 at 19:18 UTC

    Perhaps ... which defers evaluating the right hand side until the next time the ... is tested?

Re: .. operator and not including the condition of right operand
by ambrus (Abbot) on Apr 28, 2005 at 12:39 UTC

    Try ?^1(.*)? instead of /^1(.*)/ on the left hand side of the flip-flop.

Re: .. operator and not including the condition of right operand
by sh1tn (Priest) on Apr 27, 2005 at 19:50 UTC
    foreach (@aap) { if( /^1/ ... !/^0/ ){ print "$_\n" } } __END__ 1mies 2mies 1mies 6mies


      Try that with
      my @aap = qw(1one two three four 0stop done);
      The difference between the two-dot and three-dot forms is merely that the RHS will not be evaluated on the same pass as the LHS for the three-dot form. Your expression guarantees that a line beginning with 1 and any following lines beginning with 0 will be printed, plus one line that doesn't begin with 0.

      Caution: Contents may have been coded under pressure.
        ... if( /^1/ ... /^(0.*)/ ) { $1 and next; print "$_\n"; } ...


      This fails with a different input:

      % cat aap2 0aap 0noot 1mies 2mies 3mies 0aap 0noot 1mies 6mies % perl -nle 'print if (/^1/...!/^0/)' aap2 1mies 2mies 1mies 6mies
      Correct output should include 3mies.

      the lowliest monk