Тернарный, дающий другие результаты, чем If

РЕДАКТИРОВАТЬ: Для всех, кто прибыл сюда с этой проблемой, я нашел это почти дословно задокументированным в perldoc по адресу https://perldoc.perl.org/perlop#Conditional-Operator

При написании простой подпрограммы я обнаружил, что продолжаю получать неверные результаты, используя тернарный оператор, но исправляю результаты, используя более простой оператор if. Я уверен, что этому есть объяснение, но я его просто не вижу.

При подаче следующего ввода:

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

Результат должен быть 91,61.

Следующее (с использованием Ternary) приводит к 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;
};

Тем не менее, результат является ожидаемым при простом изменении на оператор if.

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;
};

Может кто-нибудь объяснить, почему это происходит? Я предполагаю, что значение, которого я не ожидаю, проникает в $_, но я не уверен, почему.

Спасибо!


person Myersguy    schedule 23.02.2021    source источник
comment
Похоже, что оператор += работает как для успешного, так и для неудачного случая в троичной версии. Может быть, кто-то еще сможет точно объяснить, почему — в любом случае, я бы не стал добавлять побочные эффекты в map. Рассмотрим функцию sum из List::Util.   -  person user3243135    schedule 23.02.2021
comment
Спасибо за понимание. Это может быть проблема приоритета, но мне было бы интересно, как ее решить, если это так. Я пытался решить эту задачу без импорта каких-либо модулей, иначе я бы просто использовал для этого List::Util.   -  person Myersguy    schedule 23.02.2021


Ответы (1)


На самом деле это вопрос приоритета. Способ диагностировать проблемы приоритета с помощью B::Deparse:

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

Ага, : имеет более высокий приоритет, чем += справа от него. Чтобы сделать то, что вы имеете в виду, используйте круглые скобки в последнем предложении тернарной операции:

$_ % 2 ? $sum+= sqrt $_ : ($sum += $_ ** 2)
person mob    schedule 23.02.2021
comment
Или, что еще лучше, просто $sum+= $_ % 2 ? квадратный $_ : $_ ** 2 - person Dave Mitchell; 23.02.2021
comment
@DaveMitchell Конечно! Я понятия не имею, почему я поместил задание внутри троичного, а не троичного внутри задания! - person Myersguy; 23.02.2021