Know How - Programming - PHP
Extrem enervierend ungewöhnliches Verhalten vom Operator "?:"
Der Operator "?:" wie in "A ? B : C" verhält sich mehr als ungewöhnlich.echo 0 ? 1 : 2 ? 3 : 4;
echo 1 ? 2 : 3 ? 4 : 5;
3
4
#include <stdio.h>
void
main(void)
{
printf ("%d\n", 0 ? 1 : 2 ? 3 : 4);
printf ("%d\n", 1 ? 2 : 3 ? 4 : 5);
}
3
2
(1 ? 2 : 3) ? 4 : 5
1 ? 2 : (3 ? 4 : 5)
if (A) B; else if (C) D; else E;
A ? B : C ? D : E
if (true) if (false) echo 1; else echo 2;
if (true) { if (false) echo 1; else echo 2; }
if (true) { if (false) echo 1; } else echo 2;
Hä? Machen sie doch, sie reduzieren zuerst den Term "A ? B : C" zu einem Ergebnis und berechnen dann "(A ? B : C) ? D : E".
Nö, machen sie nicht. Für den Menschen sieht es vielleicht so aus, aber Computer ticken anders.
Der entscheidende Punkt für den Parser ist, wenn er den ersten Doppelpunkt zu Gesicht bekommt! Der Reduce-Schritt wird also nicht erst nach dem Lesen von "C" durchgeführt, sondern schon beim Lesen des ":". Deshalb "Early".
Der Ablauf ist also folgender:
- Evaluiere A
- Ah, wir haben ein Conditional (?), also parsen wir B
- Jetzt kommt der ':', also können wir JETZT schon reduzieren.
- Sehen wir mal, was wir bisher haben: A und B.
- Ist A true? Dann haben wir schon das Ergebnis B und werfen was kommt weg.
- Ist A false? Dann schmeißen wir B weg und müssen den Rest berechnen.
- Nun evaluieren wir C
- Ah, wir haben ein Conditional (?), also parsen wir D
- Jetzt kommt der ':', also können wir JETZT schon reduzieren.
- Sehen wir mal, was wir bisher haben: C und D.
- Ist C true? Dann haben wir schon das Ergebnis D und werfen was kommt weg.
- Ist C false? Dann schmeißen wir D weg und müssen den Rest berechnen.
- Nun evaluieren wir E
- Jetzt haben wir das Ergebnis des zweiten Conditionals (C ? D : E)
- Jetzt haben wir das Ergebnis des ersten Conditionals (A ? B : (C ? D : E))
cond := expr
| cond '?' cond ':' expr
.
expr := /* eine Berechnung die kein ?: enthält */
cond := expr
| expr '?' cond ':' cond
.
expr := /* eine Berechnung die kein ?: enthält */
cond := expr iter;
iter := '?' cond ':' expr | .
expr := /* eine Berechnung die kein ?: enthält */
PS: Ja, stimmt, PHP dokumentiert verklausuliert, dass ?: linksassoziativ ist, die Klammern also auf der LINKEN Seite von ?: stehen. Sie zeigen sogar ein deutlich geklammertes Beispiel. Aber daraus schließt man trotzdem nicht auf das seltsame Verhalten dieses Operators. Ein überraschendes Verhalten einer beliebigen Sprache ist übrigens grundsätzlich ein Fehler. Fehler im Sprachdesign sind seltsam. Wen es nicht überrascht soll bitte nochmals das "if then else"-Beispiel lesen. Wenn es mehrere Möglichkeiten gibt etwas gleichartiges auszudrücken, dann muss es in jeder Schreibweise identisch ablaufen, anderenfalls ist es überraschend.