views:

1831

answers:

5

This snippet of Perl code in my program is giving the wrong result.

$condition ? $a = 2 : $a = 3 ;
print $a;

No matter what the value of $condition is, the output is always 3, how come?

Edit: I wonder if Perl is alone in this regard. Does your favorite language suffer from this bug/feature ?

+68  A: 

This is explained in the Perl documentation.

Because of Perl operator precedence the statement is being parsed as

($condition ? $a= 2 : $a ) = 3 ;

Because the ?: operator produces an assignable result, 3 is assigned to the result of the condition.

When $condition is true this means ($a=2)=3 giving $a=3

When $condition is false this means ($a)=3 giving $a=3

The correct way to write this is

$a = ( $condition ? 2 : 3 );
print $a;

We got bitten by this at work, so I am posting here hoping others will find it useful.

Pat
"When $condition is true this means $a=2=3 giving $a=3"I would have thought $a=2=3 would be an expression syntax error or lvalue required error or such... How exactly does it get evaluated?
sundar
You are right I will modify it it is ($a=2)=3 instead of $a=2=3
Pat
+17  A: 

Just to extend the previous answer... If, for whatever reason, the assignments need to be part of the conditional, you'd want to write it thusly:

$condition ? ($a=2) : ($a=3);

This would be useful if you're assigning to different variables based on the condition.

$condition ? ($a=2) : ($b=3);

And if you're choosing the variable, but assigning the same thing no matter what, you could even do this:

($condition ? $a : $b) = 3;
Tithonium
A: 

One suggestion to Tithonium's answer above:

If you are want to assign different values to the same variable, this might be better (the copy-book way):

$a = ($condition) ? 2 : 3;

Jagmal
+35  A: 

Once you have an inkling that you might be suffering from precedence problems, a trick to figure out what Perl thought you meant:

perl -MO=Deparse,-p -e '$condition ? $a= 2 : $a= 3 ; print $a;'

In your case, that'll show you:

(($condition ? ($a = 2) : $a) = 3);
print($a);
-e syntax OK

...at which point you should be saying "oh, that explains it"!

theorbtwo
Deparse is a very neat party trick.Of course, once you suspect a precedence problem, you're usually most of the way to the solution ;-)
RET
+4  A: 

Because of Perl operator precedence the statement is being parsed as:

($condition ? $a = 2 : $a ) = 3 ;

Because the ?: operator produces an assignable result, 3 is assigned to the result of the condition.

When $condition is true this means $a=2=3 giving $a=3

When $condition is false this means $a=3 giving $a=3

The correct way to write this is

$a = $condition ? 2 : 3;

In general, you should really get out of the habit of using conditionals to do assignment, as in the original example -- it's the sort of thing that leads to Perl getting a reputation for being write-only.

A good rule of thumb is that conditionals are only for simple values, never expressions with side effects. When you or someone else needs to read this code eight months from now, would you prefer it to read like this?

$x < 3 ? foo($x) : bar($y);

Or like this?

if ($x < 3) {
  $foo($x);
} else {
  $bar($y);
}
raldi