4.15. Thin Commas
Don't use commas to sequence statements .
Perl programmers from a C/C++ background are used to writing C-style for loops in Perl:
# Binary chop search...
SEARCH:
for ($min=0,$max=$#samples, $found_target=0; $min<=$max; ) {
$pos = int(($max+$min)/2);
my $test_val = $samples[$pos];
if ($target == $test_val) {
$found_target = 1;
last SEARCH;
}
elsif ($target < $test_val) {
$max = $pos-1;
}
else {
$min = $pos+1;
}
}
Each comma within the for initialization acts as a kind of "junior semicolon", separating substatements within the first compartment of the for.After seeing commas used that way, people sometimes think that it's also possible to use "junior semicolons" within a list:
print 'Sir ',
(check_name($name), $name),
', KBE';
The intent seems to be to check the person's name just before it's printed, with check_name( ) tHRowing an exception if the name is wrong (see Chapter 13). The underlying assumption is that using a comma would mean that only the final value in the parentheses was passed on to print.Unfortunately, that's not what happens. The comma actually has two distinct roles in Perl. In a scalar context, it is (as those former C programmers expect) a sequencing operator: "do this, then do that". But in a list context, such as the argument list of a print, the comma is a list separator , not technically an operator at all.The subexpression (check_name($name), $name) is merely a sublist. And a list context automatically flattens any sublists into the main list. That means that the previous example is the same as:
print 'Sir ',
check_name($name),
$name,
', KBE';
which will probably not produce the desired effect:
Sir 1Tim Berners-Lee, KBE
The best way to avoid such problems is to adopt a style that limits commas to a single role: that of separating the items of lists. Then there can be no confusion between scalar comma operators and list comma separators.If two or more statements need to be treated as a single statement, don't use scalar commas as "junior semicolons". Instead, use a do block and real semicolons:
# Binary chop search...
SEARCH:
for (do{$min=0; $max=$#samples; $found_target=0;}; $min<=$max; ) {
# etc, as before
}
print 'Sir ',
do{ check_name($name); $name; },
', KBE';
Or, better still, find a way to factor the sequence of statements out of the expression entirely:
($min, $max, $found_target) = (0, $#samples, 0);
SEARCH:
while ($min<=$max) {
# [Binary chop implementation as shown earlier]
}
check_name($name);
print "Sir $name, KBE";