Saturday, May 25, 2013

surprising interaction betwen list context and range operator

I was preparing a quiz for Perl programmers, and wanted a question on the difference between arrays and lists. So the question was : what do you get in $x and $y after

my @tab = ( 1, 3, 6..9 );
my $x   = @tab;
my $y   = ( 1, 3, 6..9 );

where I expected $x to be 6 (the number of items) and $y to be 9 (the last element), as documented in perldata. But before distributing the quiz I thought it was worth a test ... and indeed it was! To my surprise, $y turns out to be an empty string. It took me some time to find out why : with

my $y = ( 1, 3, 6, 7, 8, 9 );

the result is 9 indeed. But with

my $y = ( 1, 3, 6..9 );

the interpreter does not build the range in  list context, and then keep the last element. What happens instead is that it throws away the beginning of the list (there is even a warning about that), and then evaluates

my $y = 6..9;

in scalar context; where 6..9 is no longer a range, but a flip-flop operator. Now since the left-hand side is true, that operator is supposed to be true, right ? Well, no, because 6 is a constant, and in that case, perlop tells us that the flip-flop is  "considered true if it is equal (==) to the current input line number (the $. variable)". So $y ends up being an empty string, while

my $six  = 6;
my $nine = 9;
my $y    = $six..$nine;

would yield 1E0!

I couldn't be that nasty to the interviewed programmers, so in the end that question will not be part of the quiz.