Zum Inhalt springen
Ruby Anfรคnger 25 min

if, unless & Schleifen

Kontrollfluss in Ruby - mit Statement-Modifiers, unless, until und den vielen Iterator-Methoden auf Zahlen und Sammlungen.

Aktualisiert:
Inhaltsverzeichnis

Kontrollstrukturen in Ruby

Ruby hat die klassischen if und Schleifen - plus ein paar Besonderheiten, die dir in anderen Sprachen fehlen werden.

if / elsif / else

alter = 18

if alter >= 18
  puts "Volljaehrig"
elsif alter >= 16
  puts "Fast volljaehrig"
else
  puts "Zu jung"
end

Merke:

  • Kein then / { noetig - einfach Zeilenumbruch
  • Abschluss mit end
  • elsif, nicht else if

Statement-Modifier

Ein Ruby-Idiom: if hinter der Anweisung:

puts "Volljaehrig" if alter >= 18

Das ist bei einer Zeile lesbarer als ein vollstaendiges if/end.

if als Ausdruck

text = if alter >= 18 then "volljaehrig" else "minderjaehrig" end

# Oder mehrzeilig
text = if alter >= 18
         "volljaehrig"
       else
         "minderjaehrig"
       end

Der Ternaer-Operator geht auch:

text = alter >= 18 ? "volljaehrig" : "minderjaehrig"

unless - die Umkehrung

unless ist if not:

unless aktiv
  puts "Inaktiv"
end

# Klassisch:
puts "Inaktiv" if !aktiv

Als Statement-Modifier sehr lesbar:

return unless valid?

Lies: โ€œGib zurueck, ausser es ist valid.โ€

Vermeide unless mit else - das verwirrt:

# Schlecht
unless alter >= 18
  puts "zu jung"
else
  puts "volljaehrig"
end

# Besser: if benutzen

case / when - das Ruby-Switch

punkte = 85

note = case punkte
       when 90..100 then "A"
       when 80..89  then "B"
       when 70..79  then "C"
       when 0..69   then "F"
       else              "ungueltig"
       end
  • when-Bedingungen koennen Bereiche, Werte, Klassen, Regexe sein
  • Case-Ausdruck liefert einen Wert
  • then ist fuer einzeilige Zweige

case mit Typen

def beschreibe(wert)
  case wert
  when Integer then "Zahl: #{wert}"
  when String  then "String: #{wert.length} Zeichen"
  when nil     then "nil"
  when true, false then "Boolean"
  else              "sonst"
  end
end

Pattern Matching (Ruby 3+)

Ruby hat seit 3.0 echtes Pattern Matching:

case data
in { name: String => name, alter: Integer => alter }
  puts "#{name}, #{alter}"
in [first, *rest]
  puts "Array mit #{rest.length + 1} Elementen"
in nil
  puts "nichts da"
end

Hierdurch wird Ruby in Sachen Ausdrucksstarke mit funktionalen Sprachen wie Rust vergleichbar.

Schleifen

while und until

n = 10
while n > 0
  puts n
  n -= 1
end

# Inverses Gegenstueck
until n == 0
  puts n
  n -= 1
end

Als Statement-Modifier:

puts n while (n -= 1) > 0

loop - Endlosschleife

loop do
  print "> "
  eingabe = gets.chomp
  break if eingabe == "quit"
  puts "Du sagtest: #{eingabe}"
end

for - selten genutzt

for i in 1..5
  puts i
end

Stattdessen meistens:

(1..5).each { |i| puts i }

Der Vorteil: each schafft einen eigenen Scope, for nicht.

Iteratoren - der Ruby-Weg

Ruby-Entwickler lieben Iteratoren statt klassische Schleifen:

5.times do |i|
  puts "Iteration #{i}"   # 0, 1, 2, 3, 4
end

3.upto(7) { |n| puts n }   # 3 4 5 6 7
10.downto(5) { |n| puts n } # 10 9 8 7 6

(1..5).each { |n| puts n }

each auf Sammlungen

namen = ["Max", "Anna", "Leo"]

namen.each do |name|
  puts "Hallo, #{name}!"
end

namen.each_with_index do |name, i|
  puts "#{i}: #{name}"
end

map, select, reject, reduce

zahlen = [1, 2, 3, 4, 5]

zahlen.map    { |n| n * 2 }        # [2, 4, 6, 8, 10]
zahlen.select { |n| n.even? }      # [2, 4]
zahlen.reject { |n| n.even? }      # [1, 3, 5]
zahlen.reduce(:+)                   # 15 (Summe)
zahlen.sum                           # 15 (Kurzform)
zahlen.min                           # 1
zahlen.max                           # 5
zahlen.sort                          # [1, 2, 3, 4, 5]
zahlen.reverse                       # [5, 4, 3, 2, 1]
zahlen.include?(3)                   # true

Ketten

zahlen
  .select { |n| n > 1 }
  .map    { |n| n * 10 }
  .sum
# => 140

break, next, redo

[1, 2, 3, 4, 5].each do |n|
  next if n.even?    # ueberspringen (continue)
  break if n > 3     # abbrechen
  puts n             # 1, 3
end
  • break - Schleife verlassen
  • next - zur naechsten Iteration (wie continue)
  • redo - aktuelle Iteration neu starten (selten)

break mit Wert

ergebnis = [1, 2, 3].each do |n|
  break "Gefunden: #{n}" if n == 2
end
# "Gefunden: 2"

Exceptions

begin
  zahl = Integer(eingabe)
  puts zahl
rescue ArgumentError => e
  puts "Keine gueltige Zahl: #{e.message}"
rescue => e
  puts "Anderer Fehler: #{e.message}"
ensure
  puts "Immer ausgefuehrt"
end

Ruby-Style: begin / rescue / ensure / end.

Im Methodenrumpf geht das kompakter:

def sicher_konvertieren(text)
  Integer(text)
rescue ArgumentError
  0
end

Praktisches Beispiel

def kategorisiere(alter)
  case alter
  when ..0  then "ungueltig"
  when 1..17 then "Kind"
  when 18..64 then "Erwachsen"
  else "Senior"
  end
end

print "Alter: "
alter_text = gets.chomp

alter = Integer(alter_text) rescue nil
if alter.nil?
  puts "Bitte eine Zahl eingeben."
  exit 1
end

puts "#{alter} Jahre: #{kategorisiere(alter)}"

Zusammenfassung

  • if / elsif / else / end - klassisch, plus Statement-Modifier x if bedingung
  • unless als โ€œif notโ€-Gegenstueck
  • case/when kann Ranges, Typen und Werte matchen; ab Ruby 3 mit Pattern Matching
  • Iteratoren wie times, each, map, select sind idiomatisch - klassische for-Schleifen selten
  • next, break, redo fuer Schleifen-Kontrolle
  • begin/rescue fuer Exceptions

Im naechsten Kapitel: Methoden mit Keyword-Args, Default-Werten und Rubys Block-Magie.

Zurรผck zum Ruby Kurs