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.

Sunday, February 17, 2013

Slices of method calls within an object

Several years ago I complained that object accessors prevent you from using some common Perl idioms; in particular, you couldn't take a slice of several attributes within an object.

Now I just discovered the Want module; this is great for playing with lvalue subroutines. With the help of this module, I was finally able to use slices of method calls within an object : see . There are some caveats in comparison with a usual hash slice, but nevertheless the main point is there : you can extract some values :

  my @coordinates = mslice($point, qw/x y/);

or even assign to them:

  (mslice($point, qw/x y/)) = (11, 22);

This was written just for fun ... but in the end I might use it in real code, because in some situations, I find slices to be more compact and high-level than multiple assignment statements.