Pearl triple strangely enters the "other" position?

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 result:

 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 conclusion is the most unexpected! I don't understand why the first triple seems to always execute the "if false" sentence. It always assigns a value of 10. I also tried changing the sentence "false" to $reps = 6 , and I saw that it always got the value 6. Why does the triple logic depend on the contents of the third (if false)?

+7
source share
4 answers

Here's a simpler script that illustrates the problem:

 #!/usr/bin/perl use strict; use warnings; my $x; 1 ? $x = 1 : $x = 0; print "Without parentheses, \$x = $x\n"; 1 ? ($x = 1) : ($x = 0); print "With parentheses, \$x = $x\n"; 

He produces this conclusion:

 Without parentheses, $x = 0 With parentheses, $x = 1 

I am not sure that the connection between the destination and ?: Can be fully explained by the priority of the operator. (For example, I believe that C and C ++ can behave differently in some cases.)

Run perldoc perlop and find the "Conditional perldoc perlop " or look here ; he covers this exact question (more briefly than I am here).

In any case, I think that using the if / else would be clearer than using the ?: Operator. Or, since both true and false branches are assigned to the same variable, is it better to use ?: To change this:

 exists $testOverrides{$testName}{reps} ? $reps = $testOverrides{$testName}{reps} : $reps = $testDefaults{system_test_reps}; 

:

 $reps = ( exists $testOverrides{$testName}{reps} ? testOverrides{$testName}{reps} : $testDefaults{system_test_reps} ); 

But then again, the fact that I had to wrap the line to avoid scrolling is a good indicator that if / else will be clearer.

You can also consider using the // operator if you are not stuck in an antique version of Perl that does not support it. (It was introduced by Perl 5.10.) It is also known as the "specific or" operator. It:

 $x // $y 

equivalently

 defined($x) ? $x : $y 

So you can write:

 $reps = $testOverrides{$testName}{reps} // $testDefaults{system_test_reps}; 

This does not have exactly the same semantics, as it validates an expression using defined rather than exists ; it will behave differently if $testOverrides{$testName}{reps} exists but has the value undef .

+10
source

The -p option for B::Deparse is highlighted for such problems:

 $ perl -MO=Deparse,-p -e '$condition ? $x = $value1 : $x = $value2' (($condition ? ($x = $value1) : $x) = $value2); 

As Kate Thompson points out, it's all about priority. If the condition is false, the final destination is $x = $value2 . If the condition is true, then the assignment ($x = $value1) = $value2 - in any case, the result is the assignment of $value2 $x .

+8
source

I would do it like this (I don't mind using triple operators)

 $reps = exists($testOverrides{$testName}{reps}) ? $testOverrides{$testName}{reps} : $testDefaults{system_test_reps}; 

NTN

0
source

Thank you for providing us with a sample of your code. Now we can tear it to pieces.

Do not use the ternary operator. This is an infection left before the original C programmers feel comfortable. C used the ternary operator because it was initially more efficient than the if / else operator. Nevertheless, compilers are very well versed in code optimization, so this is no longer the case, and now it is not recommended in C and C ++ programming. Programming in C is quite complicated, since it is without triangular operators.

The Perl compiler is also extremely effective at optimizing your code, so you should always write for maximum clarity, so others that are not as good as programming and delay your code can get confused in their work.

The problem you are facing is one of the operator's priorities. You accept this:

 (exists $testOverrides{$testName}{reps}) ? ($reps = $testOverrides{$testName}{reps}) : ($reps = $testDefaults{system_test_reps}); 

So do I. In the end, this is what I mean. However, an assignment operator has a lower priority than a ternary operator. What really happens:

 (exists $testOverrides{$testName}{reps}) ? ($reps = $testOverrides{$testName}{reps}) : ($reps)) = $testDefaults{system_test_reps}); 

therefore, the final assignment always happens with $reps .

This is much better if you use if / else:

 if (exists $testOverrides{$testName}{reps}) { $reps = = $testOverrides{$testName}{reps}; } else { $reps = $testDefaults{system_test_reps}; } 

There are no problems with priority, they are easier to read and just as effective.

-2
source

All Articles