web-dev-qa-db-de.com

Ternärer Operator links Assoziativität

In der PHP Anleitung finde ich den folgenden 'User Contributed Note' unter "Operators".

Beachten Sie, dass in PHP der ternäre Operator?: Eine linke Assoziativität hat, anders als in C und C++, wo er eine rechte Assoziativität hat.

Sie können keinen Code wie diesen schreiben (wie Sie es vielleicht in C/C++ gewohnt sind):

<?php 
$a = 2; 
echo ( 
    $a == 1 ? 'one' : 
    $a == 2 ? 'two' : 
    $a == 3 ? 'three' : 
    $a == 4 ? 'four' : 'other'); 
echo "\n"; 
// prints 'four' 

Ich versuche es wirklich und es druckt wirklich four. Ich konnte jedoch den Grund dafür nicht verstehen und hatte immer noch das Gefühl, dass two oder other ausgegeben werden sollte.

Kann jemand bitte erklären, was hier passiert und warum "vier" gedruckt wird?

34
Kapil Sharma

In jeder vernünftigen Sprache ist der ternäre Operator rechtsassoziativ, sodass Sie erwarten Ihren Code folgendermaßen interpretieren würden:

$a = 2;
echo ($a == 1 ? 'one' :
     ($a == 2 ? 'two' :
     ($a == 3 ? 'three' :
     ($a == 4 ? 'four' : 'other'))));    # prints 'two'

Der ternäre Operator PHP ist jedoch seltsamerweise linksassoziativ, sodass Ihr Code tatsächlich dem folgenden entspricht:

<?php
$a = 2;
echo (((($a == 1  ? 'one' :
         $a == 2) ? 'two' :
         $a == 3) ? 'three' :
         $a == 4) ? 'four' : 'other');   # prints 'four'

Falls es immer noch nicht klar ist, sieht die Auswertung so aus:

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';
37
200_success

Weil Ihr ganzer Ausdruck ausgewertet wird, als wäre es (......) ? 'four' : 'other'. Da das erste Element wahrscheinlich etwas Wahres ist, gibt es Ihnen 'four'. In normalen Sprachen, in denen ?: die richtige Assoziativität hat, wird der gesamte Ausdruck als $a == 1 ? 'one' : (......) ausgewertet. Wenn $a nicht 1 ist, testen Sie andere Dinge.

20
Amadan

Dies ist es, was ich mir ausgedacht habe, um die Assoziativität zwischen links und rechts für den ternären Operator besser zu verstehen.

// 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"

Dies ist im Gegensatz zu:

// 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"

Wenn Sie im Beispiel PHP feststellen, dass sich der innerste Ausdruck links und im zweiten Beispiel der innerste Ausdruck rechts befindet. Bei jedem Schritt wird der nächst innerste Ausdruck ausgewertet, bis ein einzelnes Ergebnis vorliegt. Klammern sind natürlich sehr wichtig, wenn Sie ternäre Operationen in PHP verschachteln wollen!

2
Jonathon

Wenn Sie Klammern hinzufügen, wird das Problem behoben. Schauen Sie sich das folgende Beispiel an:
Ohne die Klammern ist die Note immer D, wenn die Noten größer als 50 sind, aber es funktioniert gut für Noten <= 49.
Damit das Programm so funktioniert, wie es sollte, habe ich Klammern hinzugefügt. Es ist sehr einfach zu wissen, wie viele Klammern eingegeben werden müssen, wenn es so eingegeben wird.

<?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"))))
?>
0
Dillshad Rana

Ich konnte nicht meinen Kopf um das Beispiel von wickeln:

https://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/

Also bin ich hergekommen und konnte meinen Kopf immer noch nicht darum wickeln, also musste ich durch sie hindurch gehen.

@amadan hat die beste Antwort, imo.

Dies druckt Pferd, nicht Zug.

// 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' ;

Sie können in Schritt 5 sehen, was Linksassoziativ bedeutet, wenn ich es richtig verstehe.