I have the following code:
# List of tests
my $tests = [("system_test_builtins_sin", "system_test_builtins_cos", "system_test_builtins_tan")];
# Provide overrides for certain variables that may be needed because of special cases
# For example, cos must be executed 100 times and sin only 5 times.
my %testOverrides = (
system_test_builtins_sin => {
reps => 5,
},
system_test_builtins_cos => {
reps => 100,
},
);
my %testDefaults = (
system_test_reps => 10,
);
# Execute a system tests
foreach my $testName (@$tests)
{
print "Executing $testName\n";
my $reps;
if (exists $testOverrides{$testName}{reps})
{ $reps = $testOverrides{$testName}{reps}; }
else
{ $reps = $testDefaults{system_test_reps}; }
print "After long if: $reps\n";
exists $testOverrides{$testName}{reps} ? $reps = $testOverrides{$testName}{reps} : $reps = $testDefaults{system_test_reps};
print "After first ternary: $reps\n";
exists $testOverrides{$testName}{reps} ? $reps = $testOverrides{$testName}{reps} : print "Override not found.\n";
print "After second ternary: $reps\n";
}
This gives the following output:
Executing system_test_builtins_sin
After long if: 5
After first ternary: 10
After second ternary: 5
Executing system_test_builtins_cos
After long if: 100
After first ternary: 10
After second ternary: 100
Executing system_test_builtins_tan
After long if: 10
After first ternary: 10
Override not found.
After second ternary: 10
This output is most unexpected! I don’t understand why the first ternary seems to always be executing the “if false” clause. It is always assigning a value of 10. I also tried changing the “false” clause to $reps = 6, and I saw that it always got the value of 6. Why does the ternary’s logic depend on the content of the third (if false) clause?
Here’s a simpler script that illustrates the problem:
It produces this output:
I’m not sure that the relationship between assignment and
?:can be complete explained by operator precedence. (For example, I believe C and C++ can behave differently in some cases.)Run
perldoc perlopand search for “Conditional Operator”, or look here; it covers this exact issue (more concisely than I did here).In any case, I think that using an
if/elsestatement would be clearer than using the?:operator. Or, since both the “true” and “false” branches assign to the same variable, a better use of?:would be to change this:to this:
But again, the fact that I had to wrap the line to avoid scrolling is a good indication that an if/else would be clearer.
You might also consider using the
//operator, unless you’re stuck with an ancient version of Perl that doesn’t support it. (It was introduced by Perl 5.10.) It’s also known as the “defined-or” operator. This:is equivalent to
So you could write:
This doesn’t have exactly the same semantics, since it tests the expression using
definedrather thanexists; it will behave differently if$testOverrides{$testName}{reps}exists but has the valueundef.