Условный (троичный) оператор Perl не выполняет вычисления ярлыка

#perl #conditional-operator

Вопрос:

Условный (троичный) оператор предполагает, что троичный оператор является заменой if else . Я всегда так думал, но в последнее время у меня возникла логическая проблема с этим.

Рассмотрим этот короткий сеанс отладки:

   DB<1> $s='X'

  DB<2>  1 ? $s .= '_' : $s = '_'

  DB<3> x $s
0  '_'
 

Поэтому, если 1 верно, то выражение $s .= '_' должно быть оценено (и не $s = '_' ).

Но почему это $s только '_' в конце?

Комментарии:

1. Используйте тернарный оператор только для условных выражений. Не используйте его для выбора между операторами; это затрудняет чтение кода и, как правило, создает проблемы. Используйте регулярные выражения if/else для операторов.

2. Рассматривайте тернарный оператор как инструмент для выбора значения r, а не для ветвления.

3. Если вы прочитали ссылку в вопросе, в которой говорится: » Один из способов уменьшить многословие кода Perl-заменить операторы if-else выражением условного оператора. Условный оператор (он же тернарный оператор) принимает вид: логический тест ? значение, если верно : значение, если ложно». вы можете неправильно понять 😉

4. FWIW, в C все по-другому: int a = 87; 1 ? a = 1 : a = 1; будет установлено a значение 88, а не 1. В простом C это не имеет значения, так как эта комбинация всегда является ошибкой. К сожалению, «C-подобные» языки часто ошибаются в ?: операторе, perl и php являются слишком вопиющими примерами.

Ответ №1:

Троичный условный оператор ( ?: ) имеет более высокий приоритет, чем оператор присваивания ( = ) (таблицу приоритета оператора Perl можно найти в разделе Приоритет и ассоциативность оператора perlop). Таким образом, линия

 1 ? $s .= '_' : $s = '_'
 

анализируется Perl как

 (1 ? ($s .= '_') : $s) = '_'
 

(Вы можете проверить это самостоятельно, запустив perl -MO=Deparse <your program> )

Обратите также внимание, что $s .= '_' возвращается $s с добавлением _ в конце, и что это $s может быть присвоено (с технической точки зрения, это значение lvalue). Это задокументировано в разделе Операторы присваивания perlop:

В отличие от C, оператор скалярного присваивания создает допустимое значение lvalue. Изменение назначения эквивалентно выполнению задания, а затем изменению переменной, которой оно было присвоено.

Итак, в основном ваш код выполняет

 ($s .= '_') = '_';
 

Что эквивалентно

 $s .= '_';
$s = '_';
 

Комментарии:

1. Обычно имеет смысл использовать тернарный оператор для назначения результата, а не для назначения в «ветвях». Таким образом, приоритет операторов подходит для этого случая, но в моем случае при использовании назначений в ветвях мне понадобились бы круглые скобки. Это моя вина.

Ответ №2:

Это вопрос приоритета оператора.

 $ perl -MO=Deparse,-p -e '   
> $s='X';
> $t=1;
> $t ? $s .= '_' : $s = '_';
> print $s'
($s = 'X');
($t = 1);
(($t ? ($s .= '_') : $s) = '_');
print($s);
-e syntax OK
 

Независимо от того, является ли троичное условие истинным или ложным, в конечном $s счете устанавливается значение "_" .

Чтобы сделать то, что вы собираетесь сделать, вам нужно добавить по крайней мере один набор скобок:

 1 ? $s .= '_' : ($s = '_');
 

Ответ №3:

Условный оператор оценивает только то, что необходимо, но у вас проблема с приоритетом.


Прежде всего, условный оператор действительно гарантированно использует оценку короткого замыкания, то есть он оценивает только то, что необходимо.

 $ perl -M5.010 -e'
   sub f { say "f" }
   sub g { say "g" }
   $ARGV[0] ? f() : g();
' 0
f

$ perl -M5.010 -e'
   sub f { say "f" }
   sub g { say "g" }
   say $ARGV[0] ? f() : g();
' 1
g
 

Это справедливо даже для E1 || E2 , E1 or E2 , E1 amp;amp; E2 и E1 and E2 . Они оценивают свой правый операнд только в случае необходимости.

 $ perl -M5.010 -e'
   sub f { say "f"; $ARGV[0] }
   sub g { say "g"; $ARGV[1] }
   say f() || g();
' 3 4
f
3

$ perl -M5.010 -e'
   sub f { say "f"; $ARGV[0] }
   sub g { say "g"; $ARGV[1] }
   say f() || g();
' 0 4
f
g
4
 

Вот почему вы можете open(...) or die(...) безопасно оценивать. Без короткого замыкания он будет оценивать die , был ли open он успешным или нет.


Теперь давайте объясним следующее:

 $s = "X";   1 ? $s .= "_" : $s = "_";         say $s;   # _
 

Это проблема приоритета. Вышесказанное эквивалентно

 $s = "X";   ( 1 ? ($s .= "_") : $s ) = "_";   say $s;   # _
 

$s .= "_" возвращает $s , поэтому условный оператор возвращает $s , поэтому строка _ назначается $s . Если мы добавим парены, чтобы получить желаемый синтаксический анализ, мы получим ожидаемый результат.

 $s = "X";   1 ? ($s .= "_") : ($s = "_");     say $s;   # X_
 

Альтернатива:

 $s = "X";   $s = ( 1 ? $s : "" ) . "_";       say $s;   # X_