Navigation überspringen

Logische Operatoren

Um komplexere Sachverhalte auszudrücken, reichen of "einfache" if-Abfragen nicht aus, da mehrere Dinge abgefragt und miteinander verglichen werden müssen. Solche Kombinationen sind wieder boolesche Ausdrücke und können also als Bedingung für das If verwendet werden. Die genannten Kombinationen werden über sogenannte logische Operatoren hergestellt.

Logisches UND

Nehmen wir an, du möchtest prüfen, ob die Variable x zwischen 10 und 20 liegt. In der Schule schreibt man einfach 10 < x < 20. In Processing muss dies in zwei Vergleiche zerlegt werden, nämlich in 10 < x und x < 20. Damit "10 < x < 20" wirklich gilt, müssen beide Vergleiche wahr sein. Diese Kombination nennt man das logische UND (engl. AND). Das logische UND nennt man auch einen logischen Operator. Im Code schreibt man für den UND-Operator ein doppeltes "käufmännisches Und", also &&.

int x = 5;

if (10 < x && x < 20) {
   println("Yes!");
}

Logisches ODER

Der zweite wichtige Operator ist das logische ODER (engl. OR). Nehmen wir an, du willst testen, ob die x-Koordinate eines Objekts den Bildschirmbereich verlässt, der 1024 Pixel breit ist. Dann testest du, ob x < 0 ist oder x >= 1024.

Das logische ODER schreibt man im Code mit zwei senkrechten Strichen ||

int x = 5;

if (x < 0 || x >= 1024) {
   println("Oops!");
}


Und wo ist dieses Zeichen | auf meiner Tastatur...? Auf dem Mac mit ALT+7, unter Windows mit Alt Gr + <.

Hinweis: Das logische ODER ist ein UND/ODER. Das bedeutet, wenn beide Seiten true sind, dann ist der gesamte Ausdruck true. 

Negation

Mit dem Ausrufezeichen negiert man einen Ausdruck, d.h. du kehrst den Wert um (true statt false bzw. false statt true). Das Ausrufezeichen nennt man auch den logischen Negationsoperator oder das logische NICHT (engl. NOT). Im Unterschied zu UND/ODER steht dieser Operator nicht zwischen zwei Ausdrücken, sondern vor einem einzigen Ausdruck.

Wenn du z.B. den Vergleich x < 10 negieren wilst, schreibst du  !(x < 10). Das entspricht dann offensichtlich x >= 10, d.h. das könnte man eigentlich auch direkt so (ohne Negation) hinschreiben, aber für komplexere Ausdrücke ist die Negation manchmal bequemer oder lesbarer.

Nehmen wir an, du willst das Beispiel oben "Objekt verlässt Bildschirm" einfach negieren. Das heißt, du willst Code ausführen für den Fall, dass das Objekt noch auf dem Bildschirm ist, dann schreibst du:

int x = 5;

if (! (x < 0 || x >= 1024)) {
   println("Bin auf dem Schirm!");
}

Solltest du dich fragen, wie man die obige Bedingung ohne Negation schreibt, kommst du auf diesen Ausdruck: x >= 0 && x < 1024. Das heißt, wenn man einen Ausdruck mit logischem ODER negiert, dann negiert man beide Einzelteile und man "dreht den Operator um", d.h. aus ODER wird UND und umgekehrt.

! (x < 0 || x >= 1024)

entspricht

! (x < 0) && ! (x >= 1024)

entspricht

x >=0 && x <1024

Komplexe Ausdrücke und Auswertereihenfolge

Du kannst beliebig viele logische Ausdrücke mit UND/ODER kombinieren und mit NICHT negieren. Wie wird ein solcher Ausdruck ausgewertet? Nehmen wir ein Beispiel mit zwei logischen Operatoren:

int k = 5;
float f = 2.5;
int t = 30;

if (k > 0 && f < 10.0 || t > 100) {
   println("hurra");
}

Processing nimmt sich die Bedingung:

k > 0 && f < 10.0 || t > 100

Und setzt zunächst die Variablenwerte ein:

5 > 0 && 2.5 < 10.0 || 30 > 100

Dann werden die einzelnen Vergleiche aufgelöst:

true && true || false

Jetzt gehen wir von links nach rechts vor und lösen einen Operator nach dem nächsten, also zunächst das true && true, das wird zu true. Anschließend haben wir noch:

true || false

Dies wird wiederum aufgelöst zum Ergebnis:

true

In unserem Beispiel konnten wir von links nach rechts rechnen, aber das gilt nicht immer, denn der UND-Operator hat Priorität vor ODER. Sehen wir uns das obige Beispiel leicht modifiziert an: das UND steht jetzt hinten, das ODER vorn:

k > 0 || f < 10.0 && t > 100

Wir setzen die Variablenwerte ein:

5 > 0 || 2.5 < 10.0 && 30 > 100

Und lösen die Vergleiche auf:

true || true && false

Jetzt kommt der entscheidende Unterschied: Statt von links nach rechts zu rechnen, müssen wir das UND vorziehen (das wird Präzedenz genannt):

true || true && false

Wir lösen als zunächst true && false auf zu false:

true || false

Dies wird wiederum aufgelöst zum Ergebnis:

true

Allgemein gesprochen verwendet Java bei booleschen (logischen) Ausdrücken ähnliche Regeln wie bei arithmetischen Ausdrücken (also Rechnungen mit +, -, *, /). Dabei gelten folgende Regeln:

Operator Priorität entspricht
! 3 unäres - (wie bei -3)
&& 2 *, /
|| 1 +, -

In der Tabelle bedeutet eine höhere Priorität, dass der Operator als erstes ausgewertet wird. Das bedeutet: der Negationsoperator wird immer zuerst ausgewertet. Ferner gilt: ein UND wird immer vor ODER ausgewertet.

Wenn du folgenden Ausdruck hast:

a || b && !c || d

Wird dieser so ausgewertet, als gäbe es folgende Klammern:

(a || (b && (!c))) || d

Anwendungsbeispiel: Ping-Pong

Stelle dir ein Programm vor, bei dem ein Ball von links nach rechts fliegt, an der rechten Seite abprallt, zur linken Seite zurück fliegt, dort abprallt, usw. Der speed ist dabei immer 1 bzw. -1, wenn der Ball die Richtung ändert.

Das Basisprogramm sieht zunächst mal so aus:

int x = 0;

void draw() {
   background(0);
   ellipse(x, 50, 20, 20);
   x++;
}

In dieser Version fliegt der Ball genau einmal über den Bildschirm und verschwindet dann auf Nimmerwiedersehen ...

Die Logik, die hinter dem Abprallen steht, könnte man so formulieren:

Wenn der Ball den rechten Rand erreicht,
   soll er nach links weiterfliegen.

Der erste Teilsatz übersetzt sich zu der Bedingung x > width. Der zweite Satz "nach links weiterfliegen" lässt sich realisieren, indem man die Geschwindigkeit auf -1 setzt:

if (x > width) {
   speed = -1;
}

Analog für den linken Rand:

Wenn der Ball den linken Rand erreicht,
   soll er nach rechts weiterfliegen.

Im Code:

if (x < 0) {
   speed = 1
}


Verallgemeinerung

Wenn wir die Lösung allgemeiner betrachten, sehen wir, dass in beiden Fällen einfach das Vorzeichen der Geschwindigket umgedreht wird. Aus 1 wird -1 und aus -1 wird 1. Im Code lässt sich das Umdrehen des Vorzeichens ganz elegant formulieren:

speed = -speed;

Das schöne an dieser Formulierung ist, dass es egal ist, welcher Wert in speed steht, egal ob 1 oder 3000. Wenn der Wert negativ ist, wird er positiv. Ist er positiv, wird er negativ.

Der Code würde also wie folgt aussehen:

int x = 0;
int speed = 1;

void draw() {
   background(255);
   ellipse(x, 50, 20, 20);
   x = x + speed;

   if (x > width) {
      speed = -speed;
   }

   if (x < 0) {
      speed = -speed;
   }
}


Es fällt auf, dass in beiden If-Fällen der exakt gleiche Code ausgeführt wird, also man könnte auch sagen: wenn x > width oder x < 0 der Fall ist, dann drehe das Vorzeichen um. Klarer Fall für eine boolesche Verknüpfung:

int x = 0;
int speed = 3;

void draw() {
   background(255);
   ellipse(x, 50, 20, 20);
   x = x + speed;

   if (x < 0 || x > width) {
      speed = -speed;
   }
}


Zusammenfassung

  • Zwei boolesche Ausdrücke (wie z.B. die Vergleiche x > 5 und x < 10) lassen sich mit den logischen Operatoren UND bzw. ODER kombinieren. Das Gesamte ist wieder ein boolescher Ausdruck.
  • Wir haben drei logische Operatoren kennen gelernt:
    • UND: Der Ausdruck A && B (A und B) ist genau dann wahr, wenn sowohl A als auch B wahr sind.
    • ODER: Der Ausdruck A || B (A oder B) ist genau dann wahr, wenn entweder A oder B (oder beide) wahr sind.
    • NICHT: Der Ausdruck !A ist genau dann wahr, wenn A falsch ist.
  • Ein boolescher Ausdruck ist also entweder
    • das Schlüsselwort true oder false
    • ein Vergleich von Werten (z.B. == oder <=)
    • die logische Verknüpfung von zwei booleschen Ausdrücken mit && oder ||
    • die Negation eines booleschen Ausdrucks mit !

Made with eXeLearning (New Window)