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

In the perlop documentation, it says that once the left operand of the range operator '..' is true, the operator remains true until the right operand becomes true, after which the range operator becomes false again(though it doesn't actually become false until the next time the range operator is evaluated).

So I was toyin' around with the range operator for a little while. The following code produced the expected.

print "$_\n" for (1 .. 10);
But when I tried this...
print "$_\n" for (1 .. 0);
there was no output. I don't understand why this happened since I can't see why the left operand would not be true. I figured that the loop would continue infinitely, since the right operand would never be true because of the increment. So to see if the same thing would happen with string values, I tried this...
print "$_\n" for (z .. a);
Surprisingly, the output was a 'z'. At this point, I'm really confused. The operator is apparently not behaving the same for strings and integers. Not only that, the first time the operator is evaluated returns true, and then it becomes false after only one iteration.

It also says in the perlop docs that the left operand is not evaluated while the operator is in false mode, and the right operand is not evaluated while the operator is in true mode. If that is the case, how does the loop start and stop?

I know the external behavior of using something like (1 .. 10). That is, I understand what will happen. At this point, I'm really having fun looking at the internals of Perl. I'm lost on this one though.

Anyone feel like clearing this one up?

Amel - f.k.a. - kel

Replies are listed 'Best First'.
Re: Range Operators Question
by chipmunk (Parson) on Jul 19, 2001 at 00:43 UTC
    The range operator has two different behaviors, depending on whether it is used in scalar or list context. Everything you've described above applies when the range operator is used in scalar context, whereas all your tests use it in list context..

    perlop also describes the behavior of the range operator in list context, which is simply to return a list of values counting by one from the left operand to the right operand.

    'z' .. 'a' is a special case. When the range operator is using magical autoincrement (like $var++), it stops when the string is greater than the right operand, or when the string is longer than the right operand. Thus, you'd get the same result from 'z' .. 'z' or 'z' .. '.'.

      Okay, now I see the difference between list and scalar context...I thought the whole section on the range operator referred to it in list context(and I tried to make sure that I carefully read the docs...DOH!). It is definitely more clear now.

      Still a bit confused though. After re-reading, I tried a few more examples. The goal of each is to print the numbers 5,6,7,8,9,10, one to a line.

      @arr = (1 .. 20); foreach $i ( 0 .. $#arr ) { if ( $i == (5 .. 10) ) { print "$i\n"; } } # output was "0\n" @arr = (1 .. 20); foreach $i ( 0 .. $#arr ) { if ( $i == 5 .. 10 ) { print "$i\n"; } } # output was 5-19, one integer to a line @arr = (1 .. 20); foreach $i ( 0 .. $#arr ) { if ( $i = (5 .. 10) ) { print "$i\n"; } } # no output - '$i = (5 .. 10)' never evaluated true
      Maybe I am misunderstanding how to use the operator is scalar context. The examples in the docs don't really help. Of all the examples listed, I thought the first was going to work. I'm really not understanding why it didn't.

      Thanks for your help to this point.

      Amel - f.k.a. - kel

        In scalar context, the range operator yields true or false. So comparing $i == (5..10) does not make sense. The code you want is:
        for $i (@arr) { if ($i==5 .. $i==10) { print "$i\n"; } }
        Or:
        for $i (@arr) { print "$i\n" if $i==5 .. $i==10; }

        --
        Mark Dominus
        Perl Paraphernalia

        Okay, your usage of the range operator in a scalar context isn't quite correct.

        I think what you're looking for is this:

        foreach $i ( 0 .. 19 ) { if ($i == 5 .. $i == 10) { print "$i\n"; } }
        You need to put the full comparison on each side of the range operator. Otherwise, you're doing something that's the equivalent of if ($string eq 'foo' || 'bar' || 'baz') {.

        There's one extra bit of info from perlop:

        If either operand of scalar ".." is a constant expression, that operand is implicitly compared to the $. variable, the current line number.
        So your snippets are doing implicit comparisons against $.:

        $i == (5 .. 10) compares $i to the result of the flip-flop, which flips when $. equals 5 and flops when $. equals 10.
        $i == 5 .. 10 flips when $i equals 5 and flops when $. equals 10.
        $i = (5 .. 10) assigns to $i the result of the flip-flop, which flips $. equals 5 and flops when $. equals 10.</code>

Re: Range Operators Question
by clintp (Curate) on Jul 19, 2001 at 00:47 UTC
    First of all .. is a range operator in a list context. In a list context, it returns a list of values starting at the left and counting up to the right. (Okay, for alpha it's not really "counting"...)

    In a scalar context it's a flip flop operator as you described.

    And for() gives a list context.

    You might want to pop back to perlop and re-read how it acts differently in different contexts.

Re: Range Operators Question
by lshatzer (Friar) on Jul 19, 2001 at 00:44 UTC
    If you are trying to get (z .. a) to print out as expected, descending, try print "$_\n" for reverse (a .. z);
Re: Range Operators Question
by bschmer (Friar) on Jul 19, 2001 at 00:56 UTC
    Clearly, integers are treated differently than strings. The last time I checked, print "Hello world\n" if 0; doesn't print anything, so in the (0..1) case, the left side would never evaluate to true. I'm not much of a Perl internals guy, so the best I can do is share your confusion on this point.

    In my perlop documentation it states: The right operand is not evaluated while the operator is in the "false" state, and the left operand is not evaluated while the operator is in the "true" state. This is the opposite of what you wrote. Perhaps you misread it.