Search code examples
perloperator-precedenceternary

Ternary producing different results than If


EDIT: For anyone who has arrived here with this issue, I found this documented almost verbatim in the perldoc at https://perldoc.perl.org/perlop#Conditional-Operator

In writing a simple subroutine, I found I kept getting incorrect results using the ternary operator, but correct results using a more simple if statement. I'm sure there is an explanation, but I'm simply not seeing it.

When fed the following input:

my @input = (4, 5, 7, 8, 1, 2, 3, 0);
sum_square_even_root_odd(\@input);

The result should be 91.61.

The following (using Ternary) results in 175.61.

sub sum_square_even_root_odd {
    my ($nums) = @_;
    my $sum = 0;
    map { $_ % 2 ? $sum+= sqrt $_ : $sum += $_ ** 2 } @{$nums};
    return sprintf('%.2f', $sum) + 0;
};

However, the result is what is expected when simply changed to an if statement

sub sum_square_even_root_odd {
    my ($nums) = @_;
    my $sum = 0;
    map { if( $_ % 2 ) { $sum+= sqrt $_ } else { $sum += $_ ** 2 } } @{$nums};
    return sprintf('%.2f', $sum) + 0;
};

Can someone explain why this is happening? I assume a value I am not expecting is sneaking it's way into $_, but I'm unsure as to why.

Thank you!


Solution

  • It is, in fact, a precedence issue. The way to diagnose precedence problems is with B::Deparse:

    $ perl -MO=Deparse,-p -e 'map { $_ % 2 ? $sum+= sqrt $_ : $sum += $_ ** 2 } @ARGV'
    map({((($_ % 2) ? ($sum += sqrt($_)) : $sum) += ($_ ** 2));} @ARGV);
    -e syntax OK
    

    Aha, the : has higher precedence than the += to its right. To do what you mean, use parentheses on the last clause of the ternary operation:

    $_ % 2 ? $sum+= sqrt $_ : ($sum += $_ ** 2)