Programmieren lernen mit Lua: Lektion 03: Schleifen

Übersicht | Vorherige Lektion | Nächste Lektion





Stell Dir vor, Du möchtest ein Programm schreiben, das dreimal um die Eingabe einer Zahl bittet, und am Ende die Summe aller eingegeben Zahlen ausgibt. Das Programm könnte so aussehen:

summe = 0

print("Bitte gib eine Zahl ein")
eingabe = io.read()
zahl = tonumber(eingabe)
summe = summe + zahl
print("Summe: " .. summe)

print("Bitte gib eine Zahl ein")
eingabe = io.read()
zahl = tonumber(eingabe)
summe = summe + zahl
print("Summe: " .. summe)

print("Bitte gib eine Zahl ein")
eingabe = io.read()
zahl = tonumber(eingabe)
summe = summe + zahl
print("Summe: " .. summe)

Das Programm auf repl.it

Das Programm arbeitet zwar wie gewünscht, hat aber diverse Nachteile:

  • Im Code wird ein Abschnitt drei Mal identisch wiederholt
  • Wenn Du am Eingabeprocedere etwas ändern willst, dann musst Du an drei Stellen den Code ändern
  • Du bist unflexibel, wenn Du etwa möchtest, dass 5, 9 oder 13 Zahlen eingegeben werden sollen

Lua bietet zwei Mechanismen an, um identische Codeabschnitte zu wiederholen: while– und for-Schleifen. Schauen wir uns zunächst while an.

Code wiederholen mit while

Mit while wiederholst Du einen Codeabschnitt so lange, wie eine bestimmte Bedingung wahr ist.

Das folgende Beispielprogramm wiederholt alle Eingaben wie ein Echo. Das passiert innerhalb einer Schleife. Die läuft schlichtweg so lange, bis die Nutzerin q eingegeben hat: while eingabe ~= "q". Der markierte und eingerückte Code zwischen dem do und dem end wird wiederholt. Auch hier sprechen wir – ähnlich wie bei der bedingten Ausführung in der letzten Lektion) von einem Block. Auch hier ist eine Einrückung des Codes für die Lesbarkeit dringend empfohlen.

Wenn die Eingabe gleich q ist, springt das Programm aus der while-Schleife (und dem zugehörigen Block) heraus und erreicht die letzte Zeile.

eingabe = ""

while eingabe ~= "q" do
  eingabe = io.read()
  print(eingabe)
end

print("Du hast das Programm mit 'q' beendet.")

Das Programm auf repl.it

Aus Sicht eines Nutzers könnte die Bedienung des Programms so aussehen:

$ dies
dies
$ das
das
$ q
q
Du hast das Programm mit 'q' beendet.
Mit break aus der Schleife herausspringen

Das folgende Programm verhält sich genau so wie das vorherige. Es arbeitet aber mit einer Endlosschleife: Die Bedingung true ist immer wahr. „Endlos“ ist diese Schleife aber nur im Prinzip. Denn hier wird innerhalb der Schleife nach jeder Eingabe geprüft, ob die Eingabe gleich q ist. In diesem Fall sorgt das Schlüsselwort break für ein Verlassen der Schleife. Der Vorteil besteht bei dieser Variante darin, dass wir den Bezeichner eingabe vor Beginn der Schleife nicht initialisieren müssen:

while true do
  eingabe = io.read()
  print(eingabe)
  if eingabe == "q" then
    break
  end
end

print("Du hast das Programm mit 'q' beendet.")

Das Programm auf repl.it

Zurück zu dem am Anfang vorgestellten Beispiel, bei dem eingegebene Zahlen addiert werden sollen. Mit einer while-Schleife lässt sich ein solches Programm eleganter und flexibler gestalten.

Zur Erinnerung: Die Funktion tonumber() versucht, einen String in eine Zahl umzuwandeln. Gelingt dies nicht, liefert tonumber() keine Zahl, sondern nil. Wir können also mit if zahl prüfen, ob eine ungültige Eingabe gemacht wurde. In dem Fall bricht das Programm die Schleife mit break ab,

summe = 0
 
while true do
  eingabe = io.read()
  zahl = tonumber(eingabe)
  if zahl then
    summe = summe + zahl
    print("Summe: " .. summe)
  else
    break
  end
end

print("Du hast das Programm durch eine ungültige Eingabe beendet.")

Das Programm auf repl.it

Aufgabe 1.1: Erweitere das letzte Programmbeispiel: Unter dem Bezeichner anzahl_eingaben soll die Anzahl der gültigen Eingaben gespeichert werden. Nach jeder Eingabe soll nicht nur die Summe, sondern auch die Anzahl der eingegebenen Zahlen ausgegeben werden.

summe = 0
anzahl_eingaben = 0
  
while true do
  eingabe = io.read()
  zahl = tonumber(eingabe)
  if zahl then
    summe = summe + zahl
    anzahl_eingaben = anzahl_eingaben + 1
    print("Summe: " .. summe)
    print("Anzahl der Eingaben: " .. anzahl_eingaben)
  else
    break
  end
end

print("Du hast das Programm durch eine ungültige Eingabe beendet.")

Die Lösung auf repl.it

Aufgabe 1.2: Erweitere die Lösung von Aufgabe 1.1: Es soll bei jedem Schleifendurchgang auch der Durchschnitt (also summe/anzahl_eingaben) berechnet und ausgegeben werden.

summe = 0
anzahl_eingaben = 0
  
while true do
  eingabe = io.read()
  zahl = tonumber(eingabe)
  if zahl then
    summe = summe + zahl
    anzahl_eingaben = anzahl_eingaben + 1
    durchschnitt = summe / anzahl_eingaben
    print("Summe: " .. summe)
    print("Anzahl der Eingaben: " .. anzahl_eingaben)
    print("Durchschnitt: " .. durchschnitt)
  else
    break
  end
end

print("Du hast das Programm durch eine ungültige Eingabe beendet.")

Die Lösung auf repl.it

Aufgabe 2: Schreibe eine Programm, dass einen Text per Nutzereingabe in einer while-Schleife zusammensetzt. Mit q soll aus der Schleife herausgesprungen werden:

$ Es
Es
$ war
Es war
$ einmal
Es war einmal
$ q
Ende 

text = ""

while true do
  eingabe = io.read()

  if eingabe == "q" then
    break
  else
    text = text  .. eingabe  .. " "
    print(text)
  end

end

Die markierte Zeile hängt die jeweils aktuelle Eingabe und ein Leerzeichen an den bestehenden Text an.

Die Lösung auf repl.it

Code wiederholen mit for

Die while-Schleife eignet sich vor allem für Situationen, bei denen zu Beginn der Schleife nicht klar ist, wie oft der Code wiederholt werden soll. Ist die Anzahl der gewünschten Wiederholungen bekannt, dann ist eine for-Schleife besser geeignet.

Der folgende Codeschnipsel gibt drei mal hintereinander „Ha“ aus:

for zaehler = 1,3 do
  print("Ha")
end

zaehler = 1,3 sorgt für die dreimalige Wiederholung. Wenn Du die 3 durch eine 5 ersetzt, der Code zwischen do und end fünf Mal wiederholt.

Du kannst den zaehler auch innerhalb des Blocks der for-Schleife verwenden. Das folgende Beispiel gibt die Zahlen von 1 bis 10 aus. Du kannst statt zaehler auch x, counter, index, i (oder was immer Dir beliebt) schreiben.

for zaehler = 1,10 do
  print(zaehler)
end

Der zaehler muss nicht bei 1 beginnen. Das folgende Beispiel zählt von 5 bis 10. Übrigens darfst Du den Zähler auch anders nennen. Üblich sind zum Beispiel die Bezeichnungen i oder idx (für Index):

for idx = 5,10 do
  print(idx)
end

Du hast auch die Möglichkeit, mit einer dritten Zahl eine Schrittweite anzugeben: zaehler = 0,100,10 geht von 0 nach 100 in Zehnerschritten:

for zaehler = 0,100,10 do
  print(zaehler)
end

Die Schrittweite muss keine ganze Zahl sein. So zählst Du von 0 bis 1 in Zehntelschritten:

for zaehler = 0,1,0.1 do
  print(zaehler)
end

Du kannst auch rückwärts zählen. In diesem Fall ist die Schrittweite -1:

for zaehler = 3,-3,-1 do
  print(zaehler)
end

Die for-Beispiele auf repl.it

Auch für Start- und Endpunkt sowie für die Schrittweite kannst Du selbstverständlich Bezeichner einsetzten, zum Beispiel so:

von = 5
bis = 10
schrittweite = 0.5

for idx = von,bis,schrittweite do
  print(idx)
end

Das Programm auf repl.it

Aufgabe 3: Gib die die ersten 10 vielfachen von 7 in einer for-Schleife im folgenden Format aus:

1 mal 7 ist 7
2 mal 7 ist 14
3 mal 7 ist 21
...

for zahl = 1,10 do
  print(zahl .. " mal 7 ist " .. zahl * 7)
end

Die Lösung auf repl.it

Aufgabe 4: Schreibe ein Programm, dass die Nutzerin nach Start-, Endpunkt und Schrittweite fragt und anschließend in einer for-Schleife einsetzt und die Zählerwerte ausgibt:

Startpunkt
$ 5
Endpunkt
$ 7
Schrittweite
$ 0.5
5
5.5
6
6.5
7

print("Startpunkt:")
eingabe = io.read()
von = tonumber(eingabe)

print("Endpunkt:")
eingabe = io.read()
bis = tonumber(eingabe)

print("Schrittweite:")
eingabe = io.read()
schrittweite = tonumber(eingabe)
 
for idx = von,bis,schrittweite do
  print(idx)
end

Die Lösung auf repl.it

Es ist eine nützliche Übung, wenn Du mit der Lösung von Aufgabe 4 verschiedene Start- und Endpunkte und Schrittweiten ausprobierst.

Verschachtelte for-Schleifen: Das kleine Einmaleins

In der Praxis kommt es recht häufig vor, dass der Körper einer for-Schleife wiederum eine for-Schleife enthält. Im folgenden Beispiel nutzen wir zwei solche ineinander verschachtelte Schleifen, um das kleine Einmaleins aufzuzählen:

for faktor1=1,10 do
  for faktor2=1,10 do
    print(faktor1 .. " mal " .. faktor2 .. " ist "  .. faktor1 * faktor2)
  end
end

Aufgabe 5: Verändere das letzte Programmbeispiel: Es sollen nur ungerade Zahlen aus dem Bereich 1-20 miteinander multipliziert werden.

for faktor1=1,20,2 do
  for faktor2=1,20,2 do
    print(faktor1 .. " mal " .. faktor2 .. " ist "  .. faktor1 * faktor2)
  end
end

Die Lösung auf repl.it

Aufgabe 6: Schau Dir die Lösung von Aufgabe 4 noch einmal an. Du kannst dort einmal Startpunkt, Endpunkt und Schrittweite eingeben und die entsprechende for-Schleife laufen lassen. Erweitere das Programm, damit Du beliebig oft (Stichwort Endlosschleife) neue Eingaben probieren kannst.

while(true) do  
  
  print("Startpunkt:")
  eingabe = io.read()
  von = tonumber(eingabe)
 
  print("Endpunkt:")
  eingabe = io.read()
  bis = tonumber(eingabe)
 
  print("Schrittweite:") 
  eingabe = io.read()
  schrittweite = tonumber(eingabe)
  
  for idx = von,bis,schrittweite do
    print(idx)
  end

end

Die Lösung auf repl.it

Projekt Mathetrainer

An diesem Punkt haben wir genug über Lua gelernt, um ein erstes umfangreicheres Programm zu schreiben: Einen Mathetrainer. Das Programm soll der Nutzerin Rechenaufgaben im Stil von 7 * 8 = ? stellen. Für richtige Lösungen gibt es einen Punkt. Nach einer bestimmten Anzahl von Aufgaben soll das Programm die Trefferquote ausgeben: Du hast 8 von 10 Aufgaben richtig gelöst.

Damit das Programm nicht jedes Mal die selben Aufgaben stellt, benötigen wir noch eine Funktion, die uns Zufallszahlen liefert. Das erledigt math.random(). Zur Erklärung der Schreibweise: math ist eine Erweiterung von Lua, die mathematische Funktionen bereitstellt. Falls Du mehr darüber erfahren willst: Hier findest Du eine Auflistung mit Beispielen in englischer Sprache. math.random(1, 10) liefert bei jedem Aufruf eine Zufallszahl zwischen 1 und 10.

punkte = 0
anzahl_aufgaben = 10

for zaehler=1, anzahl_aufgaben do

  faktor1 = math.random(1, 10)
  faktor2 = math.random(1, 10)

  print(faktor1 .. " * " ..  faktor2 .. " = ?" )

  eingabe = io.read()
  ergebnis = tonumber(eingabe)
  
  if ergebnis == faktor1 * faktor2 then
    print("Korrekt!")
    punkte = punkte + 1
  else
    print("Leider falsch!")
  end

end

print("Du hast " .. punkte .. " von " .. anzahl_aufgaben .. " Aufgaben gelöst.")

Zeile 4: Wenn anzahl_aufgaben gleich 10 ist, dann wird die for-Schleife 10 mal wiederholt.
Zeilen 6 und 7: Die Ermittlung der beiden Faktoren für die Rechenaufgabe
Zeilen 11 und 12: Nutzereingabe und Umwandlung in eine Zahl
Zeile 14: Wenn die eingegebene Lösung richtig ist…
Zeile 16: … erhöhe die Punktzahl um 1
Zeile 23: Berechnung und Ausgabe der Erfolgsquote

Das Programm auf repl.it

Aufgabe 7: Verpacke den Code des Mathetrainers in eine Endlosschleife. Am Ende jedes Durchgangs soll gefragt werden: „Wenn Du noch eine Runde trainieren willst, dann gib ‚ja‘ ein.“ – Bei der Eingabe von ‚ja‘ startet eine neue Runde, bei jeder anderen Eingabe soll das Programm aus der Endlosschleife springen und zuende sein.

while true do
  punkte = 0
  anzahl_aufgaben = 10

  for zaehler=1, anzahl_aufgaben do

    faktor1 = math.random(1, 10)
    faktor2 = math.random(1, 10)

    print(faktor1 .. " * " ..  faktor2 .. " = ?" )

    eingabe = io.read()
    ergebnis = tonumber(eingabe)
    
    if ergebnis == faktor1 * faktor2 then
      print("Korrekt!")
      punkte = punkte + 1
    else
      print("Leider falsch!")
    end

  end

  print("Du hast " .. punkte .. " von " .. anzahl_aufgaben .. " Aufgaben richtig gelöst.")

  print("Wenn Du noch eine Runde trainieren willst, gib 'ja' ein.")
  eingabe = io.read()
  if eingabe ~= "ja" then
    break
  end

end

Die Lösung auf repl.it

Anregungen zur Erweiterung des Mathetrainers

Es gibt viele Möglichkeiten, den Mathetrainer zu erweitern:

  • Anstatt das Programm auf den Zahlenbereich 1 bis 10 festzulegen, könntest Du einen Bezeichner maxFaktor anlegen, der die obere Grenze bestimmt. Dann könntest Du etwa das sogenannte große Einmaleins trainieren, dass die Zahlen von 1 bis 20 miteinander multipliziert.
  • Noch kniffliger wird es, wenn Du Aufgaben stellst, bei denen drei Zahlen miteinander multipliziert werden sollen: 3 * 7 * 9 = ?
  • Du könntest am Ende des Programms nicht nur die absolute Zahl der richtigen Antworten nennen, sondern das Verhältnis zwischen richtigen und falschen Antworten berechnen und ausgeben: anzahl_aufgaben/anzahlPunkte.
  • Abhängig von diesem Verhältnis könntest Du per if ... elseif ein Feedback in dieser Art geben: Wenn weniger als die Hälfte aller Aufgaben richtig gelöst wurden: „Du solltest mehr üben“ etc…

Was wir hier ausgelassen haben

Einen in bestimmten Situationen wesentlichen Punkt, den wir hier ausgelassen haben: Auch for-Schleifen lassen sich per break vorzeitig beenden. Probiere es selbst aus!

Das Schlüsselwort continue ist der kleine Bruder von break. Es bricht die Schleife nicht komplett ab, sondern die Ausführung des aktuellen Schleifendurchgangs. Das funktioniert für while– und für for-Schleifen.

Ein weiteres wichtiges Thema im Zusammenhang mit for-Schleifen: Auch zusammengesetzte Datentypen (in Lua sind das eigentlich immer Tabellen) lassen sich mit for-Schleifen durchlaufen. Wenn etwa eine Tabelle eine Liste mit Zahlen enthält, dann könnte man diese per for-Schleife aufzählen. Das zeigen wir in Lektion 5.

Navigation

Übersicht

Vorherige Lektion

Nächste Lektion


Wir bedanken uns bei der Peakboard GmbH für die freundliche Unterstützung bei der Entwicklung dieses Kurses.

Peakboard ist eine All-in-One-Lösung aus Soft- und Hardware, mit der Du Daten aus unterschiedlichen Datenquellen erhebst, auswertest und in Echtzeit auf Bildschirmen visualisierst. Mit der kostenlosen Software, dem Peakboard Designer, gestaltest Du Dein individuelles Dashboard und bindest deine Datenschnittstellen an. Die Hardware, die Peakboard Box, verarbeitet und kommuniziert die Datenströme dezentral und damit ressourcenschonend direkt am Industriearbeitsplatz. Damit sorgst Du für mehr Transparenz und optimierst so ganz einfach deine Prozesse.