Ruby/Schleife

Aus Scientia
Wechseln zu: Navigation, Suche

Schleifen

Schleifen sind Kontrollstrukturen, die einen Code mehrmals hintereinander ausführen. Mit ihnen kann man Arrays oder andere Collections durchlaufen, aber auch Prozesse, die im Programm regelmäßig ablaufen sollen (Tastenabfragen, Bewegen der Events, Neuanzeigen der Grafik usw.), wiederholen. Je nach Einsatzzweck gibt es in Ruby verschiedene Schleifentypen. In den meisten Fällen ist aber der Einsatz von Iteratoren, komplexe Methoden die oftmals auf Schleifen basieren, eleganter.

Die grundlegenden Schleifen in Ruby bestehen immer aus einer Laufbedingung und einen Schleifenkörper. Die Laufbedingung ist ein Code, der im Idealfall einen boolschen Wert (wahr oder falsch) zurückgibt. Je nach Wahrheitswert der Laufbedingung wird der Schleifenkörper ausgeführt, oder die Schleife verlassen. Ruby akzeptiert jedoch nahezu alles als Parameter für Bedingungen. So z.B.:

a = if (nil) then 3 elsif (5) then 4 else 7 end
Kernel.print(a) #=> 5

Lokale Variablen, die in einer Schleife definiert werden, dürfen auch außerhalb der Schleife verwendet werden. Bei Iteratoren gilt dies nicht.

While

Die While-Schleife, im Deutschen auch Solange-Schleife genannt, führt ihren Schleifenkörper solange aus, wie ihre Laufbedingung wahr ist.


Eine While-Schleife beginnt mit dem Schlüsselwort while. Darauf folgt die Laufbedingung. Optional darf ein do folgen. Das do ist erforderlich, will man den Schleifenkörper in der selben Zeile wie Laufbedingung schreiben. Andernfalls erfolgt nach der Laufbedingung ein Zeilenumbruch. Der Schleifenkörper wird durch das while ... do und end eingegrenzt.

a = 1
print "Schleife beginnt"
while a < 10 do
  print a
  a = a + 1
end
print "Schleife beendet"

In diesem Beispielcode wird eine Variable auf den Wert 1 gesetzt. Danach beginnt eine While-Schleife. Es erfolgt die Abfrage der Laufbedingung. Da 1 < 10 ist, springt der Interpreter in den Schleifenkörper und führt den Code zwischen der while-Zeile und der end-Zeile aus. Danach springt der Code zurück in die Laufbedingung und prüft erneut, ob a kleiner als 10 ist. Der Vorgang wiederholt sich solange, bis die Laufbedingung einen unwahren Wert zurückgibt (a also größer gleich 10 ist). Dann springt der Code in die Anweisung unter das end (verlässt also die Schleife).


Will man nur eine Anweisung wiederholen, so kann man die While-Schleife auch an das Ende der Zeile anhängen (diesmal ist das do aber verboten!).

a = 1
print "Schleife beginnt mit Wert a=1"
a = a + 1 while a < 10
print "Schleife endet mit Wert a=10"

Nun ist der Schleifenkörper also die Anweisung vor dem while-Schlüsselwort.

Manchmal ist es erforderlich, dass eine Schleife mindestens ein Mal ausgeführt wird. Ist die Laufbedingung aber schon vor dem ersten Durchlauf unwahr, so wird die Schleife nicht ausgeführt. Setzt man die Anweisung in einen begin...end-Block und hängt das While ans Ende des Blocks, so wird die Schleife mindestens ein Mal durchlaufen - selbst dann wenn die Laufbedingung unwahr ist.

a = 10
begin
 a = a + 1
 print a
end while a < 10

Hier wird also erst der Schleifenkörper ausgeführt, und danach die Laufbedingung überprüft. Ist sie unwahr, wird die Schleife verlassen, andernfalls wird der Schleifenkörper erneut ausgeführt.

Until

Die until-Schleife, im Deutschen die Wiederhole-Bis-Schleife, ist das Gegenteil der While-Schleife. Sie führt den Schleifenkörper nur aus, wenn die Laufbedingung unwahr ist. Statt While wird das Schlüsselwort until zum Einleiten der Schleife verwendet. Ansonsten gelten für sie dieselben Regeln wie für die While-Schleife.

a = 1
until a >= 10 do
  a = a + 1
end
a = a - 1 until a == 1
begin
  a = a - 1
end until a <= 1
print a #=> ergibt 0

For

Die For-Schleife ist in Ruby nur Syntaxzucker (das heißt, es ist eine verschönerte Schreibweise für etwas, das es schon gibt). Intern wird die For-Schleife als each-Iterator ausgeführt. Der einzige Unterschied zum direkten Ausführen der each Methode ist, dass lokale Variablen, die innerhalb der For-Schleife definiert wurden, auch außerhalb von ihr gültig sind.

Der Einsatzzweck für eine For-Schleife ist für gewöhnlich, einen Container mit Werten zu durchlaufen und einen Code für jeden Wert im Container auszuführen.

Syntaktisch ist eine For-Schleife folgendermaßen aufgebaut: Sie beginnt mit dem Schlüsselwort for. Danach folgt eine Liste an Parametern, die mit Kommata getrennt sind (und wie bei Iteratoren üblich) mit Klammern verschachtelt sein dürfen. In den meisten Fällen wird nur ein Parameter verlangt. Danach folgt das Schlüsselwort in und anschließend das Objekt, welches die each Methode ausführen soll. Danach darf optional ein do gesetzt werden. Daraufhin folgt der Schleifenkörper (wurde kein do gesetzt, muss vor dem Schleifenkörper ein Zeilenumbruch stehen). Die Schleife endet mit einem end Schlüsselwort.

array = ["Hallo", "schöne", "Welt"]
for wort in array do
  print wort
end
 
hash = {1 => "eins", 2 => "zwei", 3 => "drei" }
for zahl, wort in hash do
  print zahl.to_s + " heißt ausgeschrieben " + wort
end
 
range = 5..10
for zahl in range do
  print zahl
end

Im Gegensatz zu den vorherigen Schleifentypen darf das For nicht hinter dem Schleifenkörper geschrieben werden.


Die For-Schleife ist semantisch äquivalent mit der each-Methode mit vorheriger Variablendeklaration:

array = [5, 4, 8, 2, 7]
 
# die folgende For-Schleife
for zahl in array do
  maximum = zahl if maximum.nil? or maximum < zahl
end
print maximum
 
# ist semantisch äquivalent zu
maximum = nil
array.each do |zahl|
  maximum = zahl if maximum.nil? or maximum < zahl
end
print maximum
# maximum muss also vor dem Beginn der each-Schleife deklariert werden
# im Normalfall sollte man das sowieso immer tun

Wichtige wiederholende Iteratoren

Wie bereits erwähnt ist es in Ruby meistens besser Iteratormethoden statt Schleifen zu verwenden. Für Wiederholung von Code gibt es z.B. folgende Iteratormethoden:

loop

Wiederholt einen Code unendlich oft, bis die Schleife mit einer break Sprunganweisung beendet wird.

# Lose ziehen
geld = 100
loop do
  geld = geld - 5
  zufall = rand(10)
  if zufall <= 3 then
    print "Niete"
  elsif zufall <= 5 then
    print "Freilos"
    geld = geld + 5
  else zufall <= 8 then
    print "Kleiner Gewinn"
    geld = geld + 15
  else
    print "Jackpot!"
    geld = geld + 50
  end
  if geld < 5 then
    break
  end
end

times

Eine Methode der Klasse Integer. Sie wiederholt einen Code so oft, wie die Zahl des Objektes, welche die Methode ausführt. Als Parameter bekommt der Codeblock die Zahl der bereits durchgeführten Wiederholungen.

3.times do
  print "Hallo Welt"
end
#=> Hallo Welt
#=> Hallo Welt
#=> Hallo Welt
 
4.times do |i|
  print "Dies ist die " + i.to_s + "te Wiederholung"
end
#=> Dies ist die 0te Wiederholung
#=> Dies ist die 1te Wiederholung
#=> Dies ist die 2te Wiederholung
#=> Dies ist die 3te Wiederholung

upto und downto

Ebenfalls Methoden der Klasse Integer. Sie zählen aufwärts / abwärts von sich selbst bis zum angegebenen Parameter und wiederholen ihren Codeblock, dem sie als Parameter den aktuellen Zähler übergeben.

3.upto(5) do |zahl|
  print zahl
end
#=> 3
#=> 4
#=> 5
 
6.downto(4) do |zahl|
  print zahl
end
#=> 6
#=> 5
#=> 4


step

Eine Methode der Klasse Numeric, die von dem ausführenden Objekt aufwärts zählt. Im Gegensatz zu upto funktioniert die Methode auch mit Kommazahlen und man kann angeben, in was für Schritten gezählt wird.

# zähle von 3 bis 12 in 3er Schritten
3.step(12, 3) do |zahl|
  print zahl
end
#=> 3
#=> 6
#=> 9
#=> 12
 
# zähle von 0.0 bis 1.0 in 0.2er Schritten
0.0.step(1.0, 0.2) do |zahl|
  print zahl
end
#=> 0.0
#=> 0.2
#=> 0.4
#=> 0.6
#=> 0.8
#=> 1.0

Die Methode ist auch für Range Objekte definiert und verlangt dann nur die Schrittweite als Parameter.

# zähle von 3 bis 12 in 3er Schritten
(3..12).step(3) do |zahl|
  print zahl
end
#=> 3
#=> 6
#=> 9
#=> 12