Dies ist eine alte Version des Dokuments!
Inhaltsverzeichnis
Schleifen
Schleifen werden dazu benutzt, einen bestimmten Teil eines Skripts wiederholt auszuführen. Ähnlich zu Verzweigungen wird der Inhalt einer Schleife so lange ausgeführt, wie eine bestimmte Bedingung zutrifft. Auch bei Schleifen wird diese Bedingung mittels eines Booleans ausgedrückt. Es gibt in Lua meherere Möglichkeiten, eine Schleife zu formulieren. Die wollen wir im Folgenden nacheinander durchgehen.
while-Schleife
Die while-Schleife ist die intuitivste der Schleifen in Lua. Als Satz formuliert setzt sie folgende Logik um:
„Solange eine bestimmte Bedingung erfüllt ist, führe den folgenden Block aus.“
Eine while-Schleife beginnt mit dem Schlüsselwort while
. Darauf folgt die Bedingung, die geprüft werden soll. Mit do
wird die Definition der Bedingung abgeschlossen und der auszuführende Block beginnt. end
schließt wie aus Verzweigungen oder Funktionen gewohnt die Schleife ab.
Dieses Prinzip zeigt das folgende einfache Beispiel:
MyNumber = 1 while MyNumber < 42 do MyNumber = MyNumber + 1 end
Als Satz formuliert: „Solange MyNumber kleiner als 42 ist, erhöhe MyNumber um 1.“
Die Schleifenbedingung kann dabei jeder mögliche Boolean sein, zum Beispiel auch das Ergebnis einer Funktion:
-- Dieser Code hat genau die gleiche Funktion wie unser Beispiel oben function IsNumberLargeEnough(_Number) return _Number >= 42 end MyNumber = 1 while not IsNumberLargeEnough(MyNumber) do MyNumber = MyNumber + 1 end
Die Bedingung, ob der Inhalt der Schleife ausgeführt werden soll, wird immer im Voraus geprüft. Das heißt, wenn im Beispiel oben MyNumber
zu Beginn mit 42 definiert wird, wird die Zahl kein einziges Mal um 1 erhöht werden.
Hinweis: Unsere Beispiele in diesem und den folgenden Kapiteln können besser nachvollzogen werden, wenn man die Programmzeilen nebenbei ausprobieren kann. Dazu kann man diese Webseite hier verwenden:
Um dabei bei der Ausgabe ein Ergebnis zu sehen, kann man die Fnuktion print
verwenden. Will man beispielsweise die Variable MyNumber
ansehen, kann man
print(MyNumber)
schreiben. Wenn die Variable einen anderen Namen hat, muss man diesen dementsprechend austauschen.
Um die Möglichkeiten der while-Schleife besser zeigen zu können, wollen wir den Rest einer ganzzahligen Division berechnen. Diese Operation wird auch Modulo genannt. Ziel ist also für zwei Zahlen A und B herauszufinden, wie viel Rest beim Berechnen von A/B
übrig bleibt. Die Strategie unseres Algorithmus ist es, B so lange von A abzuziehen, bis B nicht mehr in A „passt“. Der Rest muss folglich der Rest der Division sein:
function Modulo(_A, _B) while _A > _B do _A = _A - _B end return _A end
Ein klassisches Beispiel für die Verwendung einer while-Schleife ist die Berechnung des größten gemeinsamen Teilers zweier Zahlen, sodass beispielsweise ein Bruch gekürzt werden kann. Dazu verwenden wir den Euklidischen Algorithmus:
function GreatestCommonDivisor(_A, _B) if _A == 0 then return _B else while _B ~= 0 do if _A > _B then _A = _A - _B else _B = _B - _A end end end return _A end
Wer Lust hat, kann sich auf der verlinkten Wikipediaseite anschauen, warum dieser Algorithmus funktioniert.
Achtung: Stelle bei jeder Formulierung einer while-Schleife sicher, dass die Schleifenbedingung auch wirklich irgendwann false wird! Andernfalls wird dein Programm die Schleife nie verlassen und darin „gefangen“ sein (Endlosschleife). Leicht modifiziert, wird das Beispiel von oben so zu einer Endlosschleife:
MyNumber = 1 while MyNumber < 42 do MyNumber = MyNumber - 1 end
Jetzt wird MyNumber
nicht mehr um 1 erhöht, sondern reduziert. Dadurch entfernt sich die Zahl immer weiter von der 42, weshalb die Schleife niemals enden kann. Die Lua-Demo wird das Programm abbrechen, aber das Siedler-Spiel wird sich aufhängen, sodass es nur mit dem Task Manager beendet werden kann!
for-Schleife
Die for-Schleife wird dazu benutzt, den eingesetzten Code-Block für eine feste Anzahl an Wiederholungen auszuführen. Die Anzahl der Wiederholungen (Iterationen) wird durch eine Zähler angegeben, der einen Startwert, einen Zielwert und eine Schrittweite hat. Beim Startwert beginnend, geht der Zähler gemäß der Schrittweite auf den Zielwert zu. Bei jedem Schritt wird der Block in der Schleife ausgeführt.
Eingeleitet wird die for-Schleife mit dem Schlüsselwort for
. Danach wird der Name einer lokalen Zählvariable angegeben, die innerhalb der Schleife benutzt werden kann. Nach do
beginnt wie gewohnt der Schleifenblock und endet mit end
.
-- Dieser Code addiert alle ungeraden Zahlen von 1 bis 10 Sum = 0 -- Counter ist der Name der Zählvariable -- Die erste Zahl nach dem = ist der Startwert -- Die zweite Zahl ist der Zielwert, getrennt durch ein Komma -- Die dritte Zahl ist die Schrittweite, getrennt durch ein Komma for Counter = 1, 10, 2 do -- In jeder Iteration wird der aktuelle Wert der Zählvariable addiert Sum = Sum + Counter end
Wenn die Schrittweite der Zählvarable 1 sein soll, kann man sie einfach weglassen:
-- Dieser Code erstellt einen String aller Zahlen von 1 bis 10 NumberString = "" -- Die Schrittweite soll hier 1 sein, also brauchen wir sie nicht extra aufzuschreiben for Counter = 1, 10 do NumberString = NumberString..Counter -- In allen außer der letzten Iteration wollen wir ein Komma, um die Zahlen zu trennen if Counter < 10 then NumberString = NumberString..", " end end
Der Variablenname Counter
ist selbstverständlich willkürlich gewählt und kann durch jeden güligen Variablennamen ersetzt werden. Viele Programmierer benutzen als Zählvariable schlicht i
.
Wir geben noch ein Beispiel, um zu zeigen, dass for-Schleifen selbstverständlich auch rückwärts zählen können, wenn die Schrittweite negativ ist:
PerfectlyBalanced = 0 -- Hier werden wieder alle Zahlen in der Zählschleife addiert -- Da die gleichen positiven und negativen Zahlen addiert werden, -- wird das Ergebnis 0 sein for i = 10, -10, -1 do PerfectlyBalanced = PerfectlyBalanced + i end
Es können auch mehrere Schleifen ineinander geschachtelt werden. Dabei ist es wichtig, dass die Zählvariablen alle unterschiedliche Namen haben!
-- Dieser Code addiert alle Zahlen a und b, wenn a und b zwischen 1 und 10 liegen -- Die Summen werden als String angezeigt for a = 1, 10 do -- Jede "Reihe" muss mit einem leeren String beginnen NumberString = "" for b = 1, 10 do -- Die einzelnen Zahlen werden mit einem Leerzeichen getrennt NumberString = NumberString..(a + b).." " end print(NumberString) end
Hinweis: Innerhalb der Schleife solltest du die Zählvariable niemals verändern:
for i = 1, 10 do i = i + 1 end
Es ist zwar prinzipiell möglich, das zu tun, führt aber meistens nur zu Problemen. Besser ist es, einfach eine andere Schrittweite zu setzen. Falls die Schrittweite sich verändern soll, bietet sich eine while-Schleife an, die eine maßgeschneiderte Zählvariable führt.
repeat-until-Schleife
Die repeat-until-Schleife ist im Prinzip nur eine „umgekehrte“ while-Schleife und findet nur selten Verwendung. Der Vollständigkeit halber wollen wir sie aber hier erklären.
Der Schleifenblock wird mit repeat
eingeleitet und mit until
beendet. Auf until
folgt die Bedingung, mit der die Schleife abbricht:
MyNumber = 1 repeat MyNumber = MyNumber + 1 until MyNumber >= 42
Wir sehen, dass diese Art der Schleife auf zwei verschiedene Arten die while-Schleife umkehrt:
- Die angegebene Bedingung in der repeat-until Schleife gibt nicht an, unter welcher Bedingung die Schleife fortgesetzt wird, sondern unter welcher Bedingung sie beendet wird. Will man eine while-Schleife in eine repeat-until-Schleife umbauen, muss man die Bedingung also genau umkehren
- Der Schleifenblock wird immer mindestens 1 mal ausgeführt. Diese Eigenschaft wird in der Syntax dadurch ausgedrückt, dass die Bedingung unten statt oben in der Schleife steht. Das Programm führt also zuerst den Block aus und prüft dann, ob eine weitere Iteration stattfinden soll.
Der letzte Punkt kann zu subtilen Unterschieden im Codeverhalten führen. Vergleiche:
MyNumber = 42 while MyNumber < 42 do MyNumber = MyNumber + 1 end
und
MyNumber = 42 repeat MyNumber = MyNumber + 1 until MyNumber >= 42
In der ersten Variante hat MyNumber
zum Schluss den Wert 42
, in der zweiten den Wert 43
! Dies ist das Resultat davon, dass die repeat-until-Schleife immer mindestens 1 mal ausgeführt wird. Obwohl die beiden Schleifenbedingungen äquivalent sind, sind die Ergebnisse unterschiedlich.
Es gibt einige Fälle, in denen eine repeat-until-Schleife sinnvoll ist. In den meisten Fällen kannst du aber davon ausgehen, dass eine while-Schleife ausreicht.
Auch mit repeat-until sind Endlosschleifen möglch. Stelle deshalb sicher, dass die Schleifenbedingung immer true werden kann!
Abgrenzung zu Funktionen
Im Artikel zu Funktionen haben wir beschrieben, dass Funktionen dazu da sind, Codeteile wiederverwertbar und wiederholt ausführbar zu machen. Schleifen scheinen eine ähnliche Funktion zu erfüllen, da auch hier kleine Codeteile wiederholt ausgeführt werden. Warum braucht man also beides?
Funktionen dienen primär dazu, Teilen von Code einen Namen zu geben. Dadurch erhält der Code Struktur und wird besser lesbar. Funktionen können außerdem mit Parametern arbeiten und ein Ergebnis zurückgeben (return
), was mit Schleifen nur über Umwege geht.
Schleifen hingegen ersparen dem Programmierer Schreibarbeit, da der Inhalt der Schleife nicht mehrmals, sondern nur einmal aufgeschrieben werden muss. Auch laufen viele Berechnungsvorschriften, wie das Beispiel oben mit dem größten gemeinsamen Teiler, bis sie fertig sind und keine feste Anzahl an Schritten.
Tatsächlich haben Schleifen und Funktionen aber einen gemeinsamen historischen Ursprung. Wer möchte, kann sich dazu den Wikipedia-Artikel zu Goto anschauen. Lua bietet keine Funktionalität für Goto (und das ist auch gut so).