Operatoren

Mittlerweile sind Operatoren in C4Script auf die gleiche Weise benutzbar wie aus diversen Programmiersprachen bzw. der Mathematik bekannt.
Es gelten also insbesondere die üblichen Prioritätsregeln (Punkt-Vor-Strich und Verwandte). Genaueres kann der unten aufgeführten Operatorenliste entnommen werden.
Um die aufeinanderfolgende Berechnung bestimmter Ausdrücke zu erzwingen, können auch Klammern gesetzt werden.
Folgende Operatoren werden unterstützt:
Priorität Operator Beschreibung Standort Typen (Rückgabe, erwartet)
15 ++ Erhöht den Wert der nachgestellten Variable um 1. prefix Referenz, Referenz
15 -- Vermindert den Wert der nachgestellten Variable um 1 prefix Referenz, Referenz
15 ~ Negiert den nachgestellten Wert bitweise prefix int, int
15 ! Negiert den nachgestelllten Wert logisch prefix bool, bool
15 + macht nichts (für Abwärtskompatibilität zu Schreibweisen wie "+5") prefix int, int
15 - bildet die Gegenzahl zum nachgestellten Wert prefix int, int
15 ++ Erhöht den Wert der vorangestellten Variable um 1. postfix (nur 1 Parameter) int, Referenz
15 -- Vermindert den Wert der vorangestellten Variable um 1. postfix (nur 1 Parameter) int, Referenz
14l ** Potenziert zwei Werte postfix int, int/int
13l / Dividiert zwei Werte durcheinander postfix int, int/int
13l * Multipliziert zwei Werte miteinander postfix int, int/int
13l % gibt den Divisionrest der Division zweier Werte zurück postfix int, int/int
12l - subtrahiert zwei Werte postfix int, int/int
12l + addiert zwei Werte postfix int, int/int
11l << führt eine Links-Bitschieben-Operation aus postfix int, int/int
11l >> führt eine Rechts-Bitschieben-Operation aus postfix int, int/int
10l < gibt zurück, ob der erste Wert kleiner ist als der zweite postfix bool, int/int
10l <= gibt zurück, ob der erste Wert kleiner oder gleich dem zweiten ist postfix bool, int/int
10l > gibt zurück, ob der erste Wert größer als der zweite ist. postfix bool, int/int
10l >= gibt zurück, ob der erste Wert größer oder gleich dem zweiten ist. postfix bool, int/int
9l == Returns whether a equals b. For proplists and arrays, pointers are compared. Use DeepEqual to compare contents. postfix bool, any/any
9l != Returns whether a is unequal to b. For proplists and arrays, pointers are compared. Use !DeepEqual to compare contents. postfix bool, any/any
8l & führt ein bitweises And aus postfix int, int/int
6l ^ führt ein bitweises XOr aus postfix int, int/int
6l | führt ein bitweises Or aus postfix int, int/int
5l && führt ein logisches And aus postfix any, any/any
4l || führt ein logisches Or aus postfix any, any/any
3l ?? Gibt den rechten Operanden zurück wenn der linke Operand nil ist, ansonsten den linken Operanden. postfix any, any/any
2r *= vervielfacht die vorangestellte Variable mit dem nachgestellten Wert postfix Referenz, Referenz/int
2r /= teilt den Wert der vorangestellten Variable durch den nachgestellten Wert postfix Referenz, Referenz/int
2r %= Schreibt den Divisionrest der Division des Wertes vorangestellten Variable mit dem nachgestellten Wert in die Variable postfix Referenz, Referenz/int
2r += erhöht die vorangestellte Variable um den nachgestellten Wert postfix Referenz, Referenz/int
2r -= vermindert den Wert der vorangestellten Variable um den nachgestellten Wert postfix Referenz, Referenz/int
2r = weist der vorangestellten Variable den nachgestellten Wert zu postfix Referenz, Referenz/any

Erklärungen und Beispiele:

Postfix oder prefix?

Diese Eigenschaft eines Operators gibt an, ob er vor(pre) oder nach(post) seinem ersten Parameter steht.
Prefix - Operatoren haben immer nur einen Parameter (nach dem Operator), während Postfix - Operatoren in der Regel 2 Parameter haben, einen vor und einen danach. (siehe auch Operatoren ++/--)

Beispiel 1:

Log(" Result: %d", 5 + 5);
Log(" Result: %d", 12 - 5);
Log(" Result: %d", 5 * 5);

Ausgabe:

 Result: 10
 Result: 7
 Result: 25
Plus, Minus, Multiplikation u.ä. Operatoren bilden die Klasse der Postfix - Operatoren. Es werden jeweils zwei Werte (die vor bzw. nach dem Operator stehen) verrechnet.

Beispiel 2:

Log(" Result: %d", -(5 + 5));
Log(" Result: %d", ~7);
Log(" Result: %d", !0);

Ausgabe:

 Result: -10
 Result: -8
 Result: 1
Bei diesen Operatoren handelt es sich um sogenannte Prefix - Operatoren. Sie stehen jeweils vor dem zu verarbeitenden Wert.

Die Operatoren ++ und --

Die Operatoren ++ bzw. -- existierten sowohl als postfix- als auch als prefix-Operatoren. Dabei haben die postfix-Varianten noch die Besonderheit, dass sie keinen nachgestellten Wert benötigen.

Beispiel: (postfix)

somevar = 0;
while(somevar++ < 10)
  Log("%d", somevar )

Beispiel 2: (prefix)

somevar = 0;
while(++somevar < 10)
  Log("%d", somevar )
Diese beiden Beispiele sind fast identisch. In jedem Fall wird bei jeder Schleifenwiederholung somevar um 1 erhöht. Das Ergebnis wird jeweils mit 10 verglichen.
Doch eine wichtige Feinheit unterscheidet die beiden Operatorenvarianten: wird der postfix-Operator benutzt, so wird jeweils alte Wert der Variable zurückgegeben. D.h. das Ergebnis der Schleife im 1. Beispiel wäre eine Zahlenreihe von 1 bis 10. Denn bei dem letzten Schleifendurchlauf ist somevar am Anfang 9. Dann wird sie um eins erhöht (somevar ist dann 10), es wird aber der alte Wert (9) zurückgegeben und mit der 10 verglichen. Die Schleife wird also ein weiteres Mal durchlaufen, und es wird der Wert 10 ausgegeben.
Anders beim 2. Beispiel. Hier läuft die Schleife von 1 bis 9. Denn wenn somevar 9 ist und erhöht wird, so wird der neue Wert, eben 10, zurückgegeben. Dieser Wert ist nicht kleiner als 10, deshalb wird die Schleife abgebrochen.

Die Operatoren &&, || und ??

Diese Operatoren haben eine Besonderheit. Wenn das Ergebnis bereits nach Auswertung des ersten Parameter feststeht, wird der zweite Parameter gar nicht erst ausgewertet. Beispielsweise explodiert ein Objekt durch dieses Script nicht, weil das Ergebnis false wäre, egal was Explode zurücklieferte:
0 && Explode(20);
Außerdem ist das Ergebnis des Operators der Wert des ersten oder zweiten Parameters, je nachdem ob einer oder beide ausgewertet wurden. Beispielsweise kann man so wenn möglich einen Ritter, und ansonsten einen Clonk erzeugen:
CreateObject(Knight,0,0,GetOwner()) || CreateObject(Clonk,0,0,GetOwner())

Der Operator ??.

Der ??-Operator kann logisches definiert-oder genannt werden. Er ist nützlich um einen Default-Wert für einen Ausdruck oder Funktionsaufruf anzugeben, wenn dieser nil ergeben könnte.

Die Gleichheits-Operatoren ==, !=, === und !==

Kurzgefasst betrachten die kürzeren Operatoren mehr Dinge als gleich. Zum Beispiel sind zwei Arrays mit dem gleichen Inhalt für == gleich, aber für === nur, wenn auf beiden Seiten des Operators dasselbe Array steht. Dies spielt hauptsächlich dann eine Rolle, wenn Arrays oder Proplisten geändert werden.

Prioritäten und Assoziativität

Dieses Thema ist nicht unbedingt zum Verständnis der Operatoren vonnöten. Es soll nur zeigen, wie die Mechanismen wie Punkt-vor-Strich u.ä. im Detail funktionieren.
Um einen längeren Ausdruck mit mehreren Operatoren ausrechnen zu können, muss entschieden werden, in welcher Reinfolge die Operatoren ausgeführt werden und welches Ergebnis welchem anderem Operator zu übergeben ist.
Allgemein gilt: Operatoren mit höherer Priorität werden vor Operatoren mit niedriger Priorität ausgeführt. Anmerkung: das gilt nicht für die Reihenfolge der Ausführung der Parameter. Diese werden ganz "normal" von links nach rechts ausgeführt.

Beispiel:

Log("%d", 5 * 4 + 3 < 6);
entspricht:
Log("%d", (((5 * 4) + 3) < 6));
Dabei taucht aber noch ein Problem auf: was ist zu tun, wenn "benachbarte" Operatoren dieselbe Priorität haben? Ist dann der linke oder der rechte Ausdruck zu klammern? Über diese Frage entscheidet die sog. Assoziativität. Ist ein Operator links-assoziativ, so hat jeweils der linke Operator Priorität (übrigens auch der Regelfall, denn meistens wird von links nach rechts gerechnet). Anders bei den rechts-assoziativen Operatoren, wo jeweils der rechte Operator Priorität hat.

Beispiel:

somevar = othervar = 1 + 2 + 3 + 4;
entspricht:
somevar = (othervar = (((1 + 2) + 3) + 4) );
Hier sieht man deutlich, dass der Operator "+" links-assoziativ ist, der Ausdruck "1 + 2 + 3" wird zu "(1 + 2) + 3".
Dagegen wird der Ausdruck "somevar = othervar = x" zu "somevar = (othervar = x)", da der Operator "=" rechts-assoziativ ist.
Die Angaben über Prioritäten bzw. Assoziativitäten finden sich in der obigen Liste.

Bitweise Operatoren

In der Liste der Operatoren fallen einige Operatoren auf, die bitweise Opererationen oder Bitschieben durchführen.
Erst mal eine kurze Beschreibung von Bits: der Computer rechnet intern im sog. binären Zahlensystem. In diesem System gibt es nur zwei Ziffern, nämlich 0 und 1. (wir rechnen üblicherweise im 10er-System, wir haben 10 Ziffern von 0 bis 9). Damit lassen sich die Zahlen folgendermaßen darstellen:

Das Binärsystem

(jeweils erst im Zehnersystem, dann im Binärsystem)
1 = 00000001
2 = 00000010
4 = 00000100
8 = 00001000
6 = 00000110
7 = 00000111
Dabei wird jede Ziffer als "Bit" bezeichnet. Eine Folge von 8 Bits (s.o.) nennt man ein "Byte".
Bitweise Operatoren bearbeiten Zahlen nun bitweise.

Beispiel:

~00000000 = 11111111
~00000100 = 11111011
~01100000 = 10011111
In diesem Beispiel handelt es sich um bitweises Not. D.h. jedes Bit wird einzeln negiert (aus 0 wird 1, aus 1 wird 0).
Bitweise Operatoren mit 2 Parametern verrechnen nun jeweils die Bits zweier Zahlen.

Beispiele:

10010010
& 01110111
= 00010010
10010010
| 01110111
= 11110111
10010010
^ 01110111
= 11100101
Diese drei Operatoren sind (der Reihe nach) Und (&), Oder (|) und Exklusiv-Oder (^).
Bei dem ersten Operator (Und) steht beim Ergebnis nur dann eine 1, wenn in beiden Eingaben an der entsprechenden Stelle ebenfalls eine 1 steht. "Und" hat also die folgende Wahrheitstabelle (senkrecht bzw. waagerecht sind die beiden möglichen Parameter jeweils gelistet, in der Tabelle selbst das Ergebnis):
& 0 1
0 0 0
1 0 1
Der Operator "Oder" gibt 1 zurück, wenn eins (oder beide) der Eingaben 1 sind:
| 0 1
0 0 1
1 1 1
Der Operator "Exklusiv-Oder" (XOr) verhält sich wie das "Oder", doch wird 0 zurückgegeben, wenn beide Parameter 0 sind.
^ 0 1
0 0 1
1 1 0

Anwendungen

In Clonk werden häufig Bitoperationen benutzt, wenn es darum geht, verschiedene Eigenschaften eines Objekts in einem Wert zu speichern. Das prominenteste Beispiel sind hier wohl die C4D(Category)-Werte. Hier repräsentiert jedes Bit des C4D-Wertes eine bestimmte Eigenschaft (für Einzelheiten siehe Entwickler-Dokumentation).
Auf diese Bits kann man recht bequem über die bitweisen Operatoren zugreifen. Ein C4D-Wert kann z.B. folgendermaßen aussehen:

Beispiel: (Wipf)

Category = 69640
Dieser Wert ergibt im Binär-System: (kann mit Windows-Taschenrechner umgerechnet werden)
10001000000001000
Hier sieht man, dass Bit 3, Bit 12 und Bit 16 gesetzt sind (es wird von rechts gezählt und mit 0 begonnen)
Dies entspricht der Reihe nach den C4D-Werten C4D_Living (Objekt ist ein Lebewesen), C4D_SelectAnimal (Objekt kann im Menu für Lebewesen hinzugefügt werden) und C4D_TradeLiving (Objekt ist ein Lebewesen, darf aber trotzdem verkauft werden).
Im Script kann nun recht einfach geprüft werden, ob ein bestimmtes Bit gesetzt ist: man benutzt den Operator "Und" (&), dem man den Wert und die "Maske" übergibt. In der Maske ist nur das Bit gesetzt, dass von Interesse ist. Auf diese Weise müssen im Ergebnis alle Bits, die nicht interessieren, 0 sein (denn sie sind es in der Maske). Ist nun das Bit, das interressiert, 1, so wird diese in das Ergebnis übernommen, ist es 0, so wird im Ergebnis dort auch 0 stehen. Im letzteren Fall ist sogar das gesamte Ergebnis 0, da alle anderen Bits ja auch 0 sind. Es genügt also zu überprüfen, ob das Ergebnis von [Val] & [Mask] ungleich null ist, um herauszufinden, ob ein bestimmtes Bit gesetzt ist.

Beispiel:

10001000000001000 (Wert)
& 00001000000000000 (Maske)
= 00001000000000000
Das Ergebnis ist in diesem Fall nicht 0, also ist das Bit, dass in der Maske gesetzt ist, auch im Wert gesetzt.
10001000000001000 (Wert)
& 00000000010000000 (Maske)
= 00000000000000000
Hier ist das Ergebnis 0, denn das Bit in der Maske ist nicht beim Wert gesetzt.
Es bleibt noch die Frage zu klären, wo man die entsprechende Maske denn herbekommt. Im Fall der Category-Werte sind diese bereits mit Namen mitgeliefert, sie können über C4D_[XXX] abgerufen werden. Wollte man z.B. herausfinden, ob es sich bei einem Objekt um ein Lebewesen handelt, so sähe der Script folgendermaßen aus:
if (GetCategory() & C4D_Living)
  ;...;
Auf diese Weise kann man also einzelne Bits gezielt prüfen. Doch wie soll man diese nun bearbeiten? Will man ein einzelnes Bit setzen, so wird der Operator "Oder" (|) benutzt. Dieser wird wieder auf Wert und Maske angewendet:
10001000000001000 (Wert)
| 00000000010000000 (Maske)
= 10001000010001000 (neuer Wert)
Auf diese Weise ist es allerdings immer nur möglich, ein bestimmtes Bit auf 1 zu setzen. War dieses bereits gesetzt, so bleibt der Wert unverändert. Will man nun ein bestimmtes Bit auf 0 setzen, so benutzt man wieder den "Und"-Operator und die inverse(logisch Not) Maske, also [Wert] & ~[Maske]:
10001000010001000 (Wert)
& 11111111101111111 (= ~00000000010000000)
= 10001000000001000 (neuer Wert)
Es ist auch möglich, ein bestimmtes Bit gezielt umzuschalten, also von 1 auf 0 und von 0 nach 1. Dies leistet der bitweise "Exklusiv-Oder"-Operator (^):
10001000000001000 (Wert)
^ 00000000010000000 (Maske)
= 10001000010001000 (neuer Wert)
10001000010001000 (Wert)
^ 00000000010000000 (Maske)
= 10001000000001000 (neuer Wert)

Bitschiebereien

Außer den Operatoren, die bitweises Manipulieren von Werten erlauben (s.o.) gibt es noch sog. Bitschiebe-Operatoren. Die Funktion dieser Operatoren beschränkt sich darauf (anschaulich gesprochen), an einen Bit-Wert Nullen anzuhängen (<<) bzw. Ziffern abzuschneiden (>>).

Beispiel:

  00001000000001000 << 2
= 00100000000100000

  00001000000001000 >> 2
= 00000010000000010
Mathematisch entspricht der Operator << der Multiplikation mit 2 ^ X (genau wie im Zehnersystem das Anhängen einer Null mit einer Multiplikation mit 10 gleichkommt) und der Operator >> einer Division mit 2 ^ X (mit anschließendem Abrunden).
Mit den Operatoren können vor allem Masken erstellt (1 << BitNr), und RGB-Farbwerte zusammengesetzt und auseinandergenommen werden.
PeterW,