#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_