Benutzer-Werkzeuge

Webseiten-Werkzeuge


scripting:tutorials:level1:loops

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen der Seite angezeigt.

Link zu der Vergleichsansicht

Nächste Überarbeitung
Vorherige Überarbeitung
scripting:tutorials:level1:loops [2023/05/27 10:45] – angelegt fritz_98scripting:tutorials:level1:loops [2024/05/13 13:21] (aktuell) fritz_98
Zeile 37: Zeile 37:
  
 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. 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:
 +
 +[[ https://www.lua.org/demo.html ]]
 +
 +Um dabei bei der Ausgabe ein Ergebnis zu sehen, kann man die Fnuktion ''print'' verwenden. Will man beispielsweise die Variable ''MyNumber'' ansehen, kann man
 +<code lua>
 +print(MyNumber)
 +</code>
 +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: 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:
Zeile 68: Zeile 82:
  
 Wer Lust hat, kann sich auf der verlinkten Wikipediaseite anschauen, warum dieser Algorithmus funktioniert. 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:
 +
 +<code lua>
 +MyNumber = 1
 +
 +while MyNumber < 42 do
 +    MyNumber = MyNumber - 1
 +end
 +</code>
 +
 +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!
  
 ---- ----
Zeile 73: Zeile 99:
 ===== for-Schleife ===== ===== for-Schleife =====
  
-Die **for**-Schleife wird dazu benutzt, den eingesetzten Code-Block für eine feste Anzahl an Wiederholungen auszuführen.+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''
 +<code lua> 
 +-- 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 
 +</code> 
 + 
 +Wenn die Schrittweite der Zählvarable 1 sein soll, kann man sie einfach weglassen: 
 +<code lua> 
 +-- 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 
 +</code> 
 + 
 +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: 
 + 
 +<code lua> 
 +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 
 +</code> 
 + 
 +Es können auch mehrere Schleifen ineinander geschachtelt werden. Dabei ist es **wichtig**, dass die Zählvariablen alle unterschiedliche Namen haben! 
 +<code lua> 
 +-- 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 
 +</code> 
 + 
 +**Hinweis**: Innerhalb der Schleife solltest du die Zählvariable niemals verändern: 
 +<code lua> 
 +for i = 1, 10 do 
 +    i = i + 1 
 +end 
 +</code> 
 +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 ===== ===== 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:
 +<code lua>
 +MyNumber = 1
 +
 +repeat
 +    MyNumber = MyNumber + 1
 +until MyNumber >= 42
 +</code>
 +
 +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:
 +<code lua>
 +MyNumber = 42
 +
 +while MyNumber < 42 do
 +    MyNumber = MyNumber + 1
 +end
 +</code>
 +
 +und 
 +
 +<code lua>
 +MyNumber = 42
 +
 +repeat
 +    MyNumber = MyNumber + 1
 +until MyNumber >= 42
 +</code>
 +
 +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 ===== ===== Abgrenzung zu Funktionen =====
 +
 +Im Artikel zu [[ scripting:tutorials:level1:functions_blocks |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 [[ https://en.wikipedia.org/wiki/Goto |Wikipedia-Artikel zu Goto]] anschauen. Lua bietet keine Funktionalität für Goto (und das ist auch gut so).
 +
 +----
 +
 +Mit sogenannten //Tables// (= Tabellen) werden im nächsten Kapitel eine mächtige und flexible Datenstruktur vorgestellt.
 +
 +[[ scripting:tutorials:level1:variable_scope | Voriges Kapitel: Lokale und Globale Variablen ]]\\
 +[[ scripting:tutorials:level1:tables | Nächstes Kapitel: Tables ]]\\
 +[[ scripting:tutorials:level1:loops | Zurück nach oben ]]
scripting/tutorials/level1/loops.1685184310.txt.gz · Zuletzt geändert: 2023/05/27 10:45 von fritz_98