Тернарный оператор оставил ассоциативность
В руководстве по PHP я нахожу следующее "примечание пользователя" в разделе "Операторы".
Обратите внимание, что в php троичный оператор?: Имеет левую ассоциативность, в отличие от C и C++, где он имеет правую ассоциативность.
Вы не можете написать такой код (как вы, возможно, привыкли в C/C++):
<?php $a = 2; echo ( $a == 1 ? 'one' : $a == 2 ? 'two' : $a == 3 ? 'three' : $a == 4 ? 'four' : 'other'); echo "\n"; // prints 'four'
Я на самом деле пытаюсь, и это действительно печатает four
, Однако я не мог понять причину этого и все еще чувствую, что это должно напечатать two
или же other
,
Может кто-нибудь объяснить, что здесь происходит и почему печатается "четыре"?
5 ответов
На любом вменяемом языке троичный оператор является ассоциативным справа, так что вы ожидаете, что ваш код будет интерпретироваться следующим образом:
$a = 2;
echo ($a == 1 ? 'one' :
($a == 2 ? 'two' :
($a == 3 ? 'three' :
($a == 4 ? 'four' : 'other')))); # prints 'two'
Тем не менее, троичный оператор PHP странно левоассоциативен, так что ваш код фактически эквивалентен этому:
<?php
$a = 2;
echo (((($a == 1 ? 'one' :
$a == 2) ? 'two' :
$a == 3) ? 'three' :
$a == 4) ? 'four' : 'other'); # prints 'four'
В случае, если это все еще не ясно, оценка идет так:
echo ((((FALSE ? 'one' :
TRUE) ? 'two' :
$a == 3) ? 'three' :
$a == 4) ? 'four' : 'other');
echo ((( TRUE ? 'two' :
$a == 3) ? 'three' :
$a == 4) ? 'four' : 'other');
echo (( 'two' ? 'three' :
$a == 4) ? 'four' : 'other');
echo ( 'three' ? 'four' : 'other');
echo 'four';
Потому что все ваше выражение оценивается так, как если бы оно было (......) ? 'four' : 'other'
, Поскольку первый элемент, вероятно, является чем-то правдивым, он дает вам 'four'
, На более разумных языках, где ?:
имеет правильную ассоциативность, все выражение оценивается так, как если бы оно было $a == 1 ? 'one' : (......)
где если $a
не является 1
продолжаешь тестировать другие вещи.
Это то, что я придумал, чтобы помочь себе понять левую и правую ассоциативность для троичного оператора.
// PHP
$a = "T";
$vehicle = $a == "B" ? "bus" :
$a == "A" ? "airplane" :
$a == "T" ? "train" :
$a == "C" ? "car" :
$a == "H" ? "horse" : "feet";
// (as seen by the PHP interpreter)
// INITIAL EXPRESSION: ((((($a == "B" ? "bus" : $a == "A") ? "airplane" : $a == "T") ? "train" : $a == "C") ? "car" : $a == "H") ? "horse" : "feet");
// STEP 1: (((((FALSE ? "bus" : FALSE) ? "airplane" : TRUE) ? "train" : FALSE) ? "car" : FALSE) ? "horse" : "feet")
// STEP 2: ((((FALSE ? "airplane" : TRUE) ? "train" : FALSE) ? "car" : FALSE) ? "horse" : "feet")
// STEP 3: (((TRUE ? "train" : FALSE) ? "car" : FALSE) ? "horse" : "feet")
// STEP 4: (("train" ? "car" : FALSE) ? "horse" : "feet")
// STEP 5: ("car" ? "horse" : "feet")
// FINAL EVALUATION: ("horse")
// If you used the initial expression here (with the parenthesis) in a different language, it would also evaluate to "horse."
echo $vehicle; // gives us "horse"
Это в отличие от:
// EVERY OTHER LANGUAGE
var a = "T";
var vehicle = a == "B" ? "bus" :
a == "A" ? "airplane" :
a == "T" ? "train" :
a == "C" ? "car" :
a == "H" ? "horse" : "feet";
// (as seen by the other language's interpreter)
// INITIAL EXPRESSION: (a == "B" ? "bus" : (a == "A" ? "airplane" : (a == "T" ? "train" : (a == "C" ? "car" : (a == "H" ? "horse" : "feet")))));
// STEP 1: (FALSE ? "bus" : (FALSE ? "airplane" : (TRUE ? "train" : (FALSE ? "car" : (FALSE ? "horse" : "feet")))))
// STEP 2: (FALSE ? "bus" : (FALSE ? "airplane" : (TRUE ? "train" : (FALSE ? "car" : "feet"))))
// STEP 3: (FALSE ? "bus" : (FALSE ? "airplane" : (TRUE ? "train" : "feet")))
// STEP 4: (FALSE ? "bus" : (FALSE ? "airplane" : "train"))
// STEP 5: (FALSE ? "bus" : "train")
// FINAL EVALUATION: ("train")
// If you used the initial expression here (with the parenthesis) in PHP, it would also evaluate to "train."
console.log(vehicle); // gives us "train"
Если вы заметили, что в примере PHP самое внутреннее выражение находится слева, а во втором примере самое внутреннее выражение справа. Каждый шаг оценивает следующее самое внутреннее выражение, пока не будет единственного результата. Скобки явно очень важны, если вы собираетесь вкладывать троичные операции в PHP!
Я не мог обернуть голову вокруг примера из:
https://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/
Поэтому я пришел сюда и все еще не мог обернуть голову вокруг, поэтому мне пришлось пройти через это.
У @amadan лучший ответ, IMO.
Это печатает лошадь, а не поезд.
// 0
$arg = 'T';
$vehicle =
$arg == 'B' ? 'bus' :
$arg == 'A' ? 'airplane' :
$arg == 'T' ? 'train' :
$arg == 'C' ? 'car' :
$arg == 'H' ? 'horse' :
'feet' ;
// 1
$vehicle =
> FALSE ? 'bus' :
$arg == 'A' ? 'airplane' :
$arg == 'T' ? 'train' :
$arg == 'C' ? 'car' :
$arg == 'H' ? 'horse' :
'feet' ;
// 2
$vehicle =
FALSE ? 'bus' :
> FALSE ? 'airplane' :
$arg == 'T' ? 'train' :
$arg == 'C' ? 'car' :
$arg == 'H' ? 'horse' :
'feet' ;
// 3
$vehicle =
> (FALSE? 'bus' : FALSE? 'airplane' : TRUE)? 'train' :
$arg == 'C' ? 'car' :
$arg == 'H' ? 'horse' :
'feet' ;
// 4
$vehicle =
> true ? 'train' :
$arg == 'C' ? 'car' :
$arg == 'H' ? 'horse' :
'feet' ;
// 5
$vehicle =
> ('train' : $arg == 'C') ? 'car' :
$arg == 'H' ? 'horse' :
'feet' ;
// 6
$vehicle =
> (true ? 'car' : $arg == 'H') ? 'horse' :
'feet' ;
// 7
$vehicle =
> (true) ? 'horse' : 'feet' ;
Вы можете увидеть, что осталось ассоциативного средства в шаге 5, если я правильно понимаю.
Если вы добавите скобки, проблема будет решена. Посмотрите на следующий пример:
Без скобок оценка всегда D, когда оценки больше 50, но она отлично работает для оценок<= 49.
Чтобы программа работала должным образом, я добавил круглые скобки. Очень легко узнать, сколько скобок нужно ввести, если оно набрано так.
<?php
$marks_obtained = 65;
$grade = null;
//Use parentheses () otherwise the final grade shown will be wrong.
//Excluding the first line, for each additional line,
//we add a parenthesis at the beginning of each line and a parenthesis at the end of the statement.
echo $grade = $marks_obtained >= 90 && $marks_obtained <= 100 ? "A+"
: ($marks_obtained <= 89 && $marks_obtained >= 80 ? "A"
: ($marks_obtained <= 79 && $marks_obtained >= 70 ? "B"
: ($marks_obtained <= 69 && $marks_obtained >= 60 ? "C"
: ($marks_obtained <= 59 && $marks_obtained >= 50 ? "D"
: "F"))))
?>