Grundlagen
Diese Seite erklärt die ersten Schritte der imperativen Programmierung mit dem Java-Hamster-Modell.
1. Die main-Prozedur
Wenn ein Hamsterprogramm ausgeführt wird, ruft der Rechner die main-Prozedur auf. Eine Prozedur besteht immer aus zwei Teilen:
- Name (hier
main) - Rumpf: Dies ist ein Block von Anweisungen, welcher
durch geschweifte Klammern
{und}eingefasst wird.
2. Kompilieren
Bevor das Hamsterprogramm ausgeführt werden kann, muss es in eine maschinenlesbare Form gebracht werden. Du kannst es dir so vorstellen: Java ist eine Programmuiersprache, die wir Menschen gut verstehen und die sehr ausdrucksstark ist. Der Computer besteht intern aber aus einem großen Schaltkreis (genannt Prozessor), und dieser versteht nur sehr einfache Befehle. Deshalb muss das Programm in eine einfachere Sprache übersetzt werden, auch kompilieren genannt. Im Hamster-Simulator geschieht dies mit Klick auf
.
Manchmal schlägt das Kompilieren fehl, weil ein Fehler im Programmtext festgestellt wird. Diesen musst du dann beheben. Z.B. sagt der folgende Syntaxfehler, dass ein Semikolon in Zeile 17 hinter linksUm() fehlt. Das Wort „Syntax“ steht allgemein für „Grammatik“/„Rechtschreibung“.
3. Ausführen
void main()
{
vor();
nimm();
linksUm();
linksUm();
vor();
gib();
}
Nach der Kompilierung kann das Programm ausgeführt werden
. Wie bereits beschrieben, beginnt die Ausführung in der main-Prozedur. Der Rumpf der main-Prozedur besteht nun in der Regel aus einer Sequenz von Befehlen, welche hintereinander von oben nach unten abgearbeitet werden. Diese Befehle sind von unterschiedlicher Bauart; am Wichtigsten sind zunächst aber die Grundbefehle vor(); und linksUm(); und nimm(); und gib(); . Das folgende Programm lässt den Hamster also zunächst einen Schritt nach vorne machen, dann ein Korn aufnehmen, anschließend umdrehen (zweimal nach links drehen), wieder einen Schritt nach vorne machen und abschließend ein Korn ablegen.
Ein Befehl kann manchmal zum Absturz des Programms führen. Dies ist z.B. der Fall, wenn der Hamster vor einer Mauer steht und dann mit dem vor();-Befehl in die Mauer „crasht“. In diesem Fall tritt eine sogenannte MauerDaException auf und das Programm ist vorzeitig beendet. Falls das obige Bsp.programm im linksstehenden Territorium ausgeführt würde, wäre dies die Folge:
void main()
{
vor();
rechtsUm();
vor();
rechtsUm();
}
void rechtsUm()
{
linksUm();
linksUm();
linksUm();
}
4. Prozeduren definieren und aufrufen
Neben der main-Prozedur lassen sich weitere Prozeduren definieren. Eine definierte Prozedur kann als eine Befehlsabfolge verstanden werden, die mit einem Namen versehen wird. In den Zeilen 9 bis 14 wird die Prozedur namens rechtsUm definiert als Sequenz von drei linksUm();-Befehlen.
Von nun an steht der Befehl rechtsUm(); zur Verfügung, was einem einiges an Tipparbeit erspart. Man nennt den Befehl rechtsUm(); auch einen Prozeduraufruf (Zeilen 4 und 6) der Prozedur rechtsUm.
Eine Prozedur, die aufgerufen wird, muss immer auch definiert worden sein. Die Reihenfolge der Definitionen von main und rechtsUm ist dabei egal, die Prozedur rechtsUm hätte auch vor main definiert worden können. Die beiden Prozeduren stehen „gleichberechtigt“ untereinander; wichtig ist nur, dass sie jeweils für sich genommen nicht „auseinandergerissen“ werden und auch nicht ineinander „verschachtelt“ werden.
Wird eine selbst definierte Prozedur aufgerufen (z.B. in Zeile 4), so
- springt der Rechner nach vorne zur Prozedurdefinition (Zeile 9),...
- arbeitet den Rumpf ab (Zeile 11 bis 13), und ...
- springt wieder zurück zur Aufrufstelle, um den nächsten Befehl abzuarbeiten (Zeile 5).
5. Fallunterscheidungen und Testbefehle
Häufig kommt es vor, dass der Hamster auf seine Umwelt reagieren soll. Z.B.: „Nimm zunächst ein Korn. Falls die nächste Kachel frei ist, gehe nach vorne. Falls nicht, gehe nach links. Lege anschließend ein Korn ab.“
Hierfür gibt es die Fallunterscheidung, auch Konditional, Verzweigung, bedingte Anweisung genannt.
void main()
{
nimm();
if (vornFrei())
{
vor();
}
else
{
linksUm();
vor();
}
gib();
}
Hinter dem if steht die Bedingung, welche geprüft werden soll (hier: „ist die nächste Kachel frei?“). Anschließend folgen zwei Anweisungsblöcke, welche wie beim Prozedurrumpf durch { und } eingerahmt werden.
Der erste Anweisungsblock (Zeile 4-7) heißt Konsequenz (if-Block) und wird nur dann ausgeführt, wenn die Bedingung erfüllt ist.
Der zweite Anweisungsblock (Zeile 9-12) heißt Alternative (else-Block) und wird nur dann ausgeführt, wenn die Bedingung nicht erfüllt ist.
Im rechtsstehenden Programm prüft der Rechner also in Zeile 4, ob sich der Hamster vor einer Mauer befindet oder nicht. Falls ja, so springt er zur Konsequenz und führt diese aus (Zeile 6). Falls nein, so springt er zur Alternative und führt diese aus (Zeile 10-11). In beiden Fällen wird der Programmablauf anschließend in Zeile 13 fortgeführt.
Falls die Alternative keine Befehle enthält, kann man sie (mitsamt else) weglassen.
Die folgenden weiteren sogenannten Testbefehle können als Bedingung verwendet werden: vornFrei() und kornDa() (liegt mindestens ein Korn auf der aktuellen Kachel?) und maulLeer() (hat der Hamster mindestens ein Korn in seiner Backe liegen?).
Beachte, dass auf einen Testbefehl kein Semikolon folgt.
6. Schleifen
void main()
{
// Solange die
// Bedingung
// erfuellt ist, ...
while (vornFrei())
{
// ...fuehre den
// Schleifenrumpf
// aus.
vor();
}
nimm();
}
Um Anweisungen wiederholt auszuführen, kann die while-Schleife verwendet werden. Sie besteht aus einer Schleifenbedingung (Zeile 6) sowie einem Schleifenrumpf (Zeile 8-11). Wenn der Rechner zu Zeile 6 gelangt, wird die Schleife wie folgt ausgeführt.
- Werte Schleifenbedingung aus (Zeile 6).
-
- Falls Bedingung zu
trueauswertet: Führe einmal gesamten Schleifenrumpf aus. Springe dann zurück zu Zeile 6. - Falls Bedingung zu
falseauswertet: Verlasse Schleife und springe zu Zeile 13.
- Falls Bedingung zu
7. Logische Ausdrücke
Jeder Testbefehl wertet immer zu „wahr“ oder „falsch“ aus. In Java entspricht dies den beiden Wahrheitswerten true und false. Es ist möglich, mehrere Testbefehle zu kombinieren. Diese durch logische Operatoren kombinierten Testbefehle werden logische Ausdrücke genannt (das Gebiet der Logik beschäftigt sich mit wahren und falschen Aussagen):
| Operator | Bedeutung | Syntax | Beispiel |
|---|---|---|---|
| Negation | „nicht“ | !a |
!vornFrei() |
| Konjunktion | „und“ | a && b |
vornFrei() && maulLeer() |
| Disjunktion | „oder“ | a || a |
vornFrei() || maulLeer() |
void main()
{
if (!maulLeer())
{
gib();
}
}
Im rechtsstehenden Programm wird geprüft, ob „nicht das Maul leer ist“ − mit anderen Worten, ob der Hamster noch mindestens ein Korn in seinen Backen vorrätig hat. Falls dem so ist, legt er dieses Korn ab (Zeile 5).
Hinweis: Die Disjunktion, z.B. vornFrei() || maulLeer(), ist erfüllt, wenn mindestens eine der beiden Bedingungen erfüllt ist − insbesondere dürfen also auch beide Bedingungen erfüllt sein.
8. Schachtelung
void main()
{
while (vornFrei())
{
vor();
if (kornDa())
{
nimm();
}
}
}
Die bisher kennengelernten Programmkonstrukte („Bausteine“) lassen sich flexibel kombinieren, auch Schachtelung genannt. Z.B. kann eine Schleife mit einer Fallunterscheidung geschachtelt werden: So lange vorne frei ist, soll der Hamster nach vorne gehen und dabei anschließend ein Korn fressen, falls dies möglich ist.
Durch Schachtelungen lassen sich Programmkonstrukte (z.B. Fallunterscheidung, Schleife, ...) auf vielfältige Weise kombinieren. So entsteht ein unbegrenztes Potential an Programmen, die du schreiben kannst!