Lektion 1.5 · Stufe 1 – Swift-Fundament

Wiederholungen: Schleifen

ca. 15 Minuten Lesezeit

Lernziel

Nach dieser Lektion kannst du Code mit for-in eine bestimmte Anzahl von Malen wiederholen, mit while so lange wiederholen, bis eine Bedingung erfüllt ist, und Schleifen mit break und continue steuern.

Das Problem: Copy & Paste

Stell dir vor, du willst einen Countdown von 5 bis 1 ausgeben. Mit dem, was du bisher weißt:

Swift
print(5)
print(4)
print(3)
print(2)
print(1)
print("Start!")

Für fünf Zahlen geht das noch. Aber für 100? Oder für eine Anzahl, die du vorher gar nicht kennst, etwa so viele Zeilen, wie ein Nutzer Einträge hat?

Programmierer haben eine eiserne Regel: Wiederhole dich nicht (englisch Don't Repeat Yourself, kurz DRY). Wenn du Code kopierst und nur eine Kleinigkeit änderst, gibt es fast immer einen besseren Weg. Für Wiederholungen ist das die Schleife.

for-in: Zählen

Die häufigste Schleife in Swift:

Swift
for number in 1...5 {
    print(number)
}

Ausgabe:

Text
1
2
3
4
5

So liest du das: „Für jede Zahl in dem Bereich von 1 bis 5: Führe den Block aus.“

Bei jedem Durchlauf bekommt number den nächsten Wert aus dem Bereich. Den Namen number darfst du frei wählen. Er gilt nur innerhalb der geschweiften Klammern.

Bereiche kennst du aus Lektion 1.4:

Swift
for i in 0..<3 {
    print("Durchlauf \(i)")
}
// Durchlauf 0
// Durchlauf 1
// Durchlauf 2

Tipp

0..<3 läuft dreimal: 0, 1, 2. Diese Form siehst du in der Programmierung sehr oft, weil Computer meist bei 0 zu zählen beginnen. Mehr dazu in Lektion 1.6.

Wenn du die Zahl gar nicht brauchst

Manchmal willst du etwas nur x-mal wiederholen und brauchst den Zähler nicht. Dann schreibst du statt eines Namens einen Unterstrich:

Swift
for _ in 1...3 {
    print("Hip hip hurra!")
}

Der _ bedeutet in Swift: „Hier steht etwas, das mich nicht interessiert.“ Er wird dir noch öfter begegnen.

Rückwärts zählen und in Schritten

Für unseren Countdown brauchen wir die Zahlen rückwärts:

Swift
for number in (1...5).reversed() {
    print(number)
}
print("Start!")

Und für Schritte, etwa nur jede zweite Zahl, gibt es stride:

Swift
for number in stride(from: 0, to: 10, by: 2) {
    print(number)   // 0, 2, 4, 6, 8
}

stride(from:to:by:) zählt ohne den Endwert, stride(from:through:by:) mit.

Mit Werten rechnen

Schleifen sind besonders mächtig in Kombination mit Variablen. Hier wird die Summe der Zahlen von 1 bis 100 berechnet:

Swift
var sum = 0

for number in 1...100 {
    sum += number
}

print(sum)   // 5050

Die Variable sum steht vor der Schleife. Stünde sie innerhalb, würde sie bei jedem Durchlauf neu bei 0 beginnen.

Über Texte laufen

for-in funktioniert nicht nur mit Zahlen, sondern mit allem, was aus mehreren Teilen besteht, zum Beispiel mit den Buchstaben eines Strings:

Swift
for letter in "Swift" {
    print(letter)
}
// S, w, i, f, t (je in einer Zeile)

In der nächsten Lektion lernst du Arrays kennen, Listen von Werten. Mit for-in gehst du sie Element für Element durch. Das ist mit Abstand der häufigste Einsatz von Schleifen.

while: Solange …

Manchmal weißt du vorher nicht, wie oft etwas wiederholt werden muss. Du weißt nur, wann es aufhören soll. Dafür gibt es while:

Swift
var balance = 100
var months = 0

while balance > 0 {
    balance -= 30
    months += 1
}

print("Das Geld reicht \(months) Monate.")   // 4

So funktioniert es: Vor jedem Durchlauf prüft Swift die Bedingung. Ist sie true, läuft der Block. Ist sie false, geht es nach der Schleife weiter.

Endlosschleifen

Wird die Bedingung nie false, läuft die Schleife für immer:

Swift
var count = 0
while count < 5 {
    print(count)
    // count += 1 vergessen!
}

Dein Playground hängt sich dann auf. Klick auf den Stop-Button. Prüfe bei jeder while-Schleife: Ändert sich im Block etwas, das die Bedingung irgendwann false macht?

repeat-while: Mindestens einmal

repeat-while prüft die Bedingung erst nach dem Durchlauf. Der Block läuft also mindestens einmal:

Swift
var dice = 0

repeat {
    dice = Int.random(in: 1...6)
    print("Gewürfelt: \(dice)")
} while dice != 6

Würfeln, bis eine 6 kommt. Ohne den ersten Wurf gäbe es ja nichts zu prüfen. Int.random(in:) liefert übrigens eine Zufallszahl im angegebenen Bereich.

for oder while?

  • for-in, wenn du weißt, wie oft oder worüber du laufen willst. In der Praxis ist das der Normalfall.
  • while, wenn du nur weißt, bis wann.

Schleifen steuern: break und continue

continue: Diesen Durchlauf überspringen

Swift
for number in 1...10 {
    if number % 2 == 0 {
        continue
    }
    print(number)
}
// 1, 3, 5, 7, 9

Bei geraden Zahlen springt continue sofort zum nächsten Durchlauf. Das print wird übersprungen.

break: Schleife ganz beenden

Swift
let numbers = 1...100

for number in numbers {
    if number * number > 50 {
        print("Erste Zahl, deren Quadrat größer als 50 ist: \(number)")
        break
    }
}
// Erste Zahl, deren Quadrat größer als 50 ist: 8

Sobald das Gesuchte gefunden ist, beendet break die Schleife. Die restlichen Zahlen werden gar nicht mehr angeschaut.

Verschachtelte Schleifen

Schleifen können in Schleifen stehen. Ein klassisches Beispiel ist das kleine Einmaleins:

Swift
for row in 1...3 {
    for column in 1...3 {
        print("\(row) × \(column) = \(row * column)")
    }
}

Für jeden Durchlauf der äußeren Schleife läuft die innere Schleife komplett durch. Bei 3 × 3 sind das also 9 Ausgaben.

Typische Fehler

Bereich falsch herum

Swift
for i in 5...1 {
    print(i)
}

Fehlermeldung

Laufzeitfehler: Range requires lowerBound <= upperBound

Der Code stürzt ab. Ein Bereich muss immer vom kleineren zum größeren Wert gehen. Für rückwärts: (1...5).reversed().

Variable in der Schleife statt davor

Swift
for number in 1...3 {
    var sum = 0
    sum += number
}
print(sum)

Fehlermeldung

Cannot find 'sum' in scope

sum existiert nur innerhalb der Schleife und beginnt bei jedem Durchlauf wieder bei 0. Leg die Variable vor der Schleife an.

Schleifenvariable ändern

Swift
for i in 1...5 {
    i += 1
}

Fehlermeldung

Left side of mutating operator isn't mutable: 'i' is a 'let' constant

Die Schleifenvariable ist eine Konstante. Swift setzt sie selbst bei jedem Durchlauf. Du darfst sie lesen, aber nicht ändern.

Endlosschleife

Der Playground reagiert nicht mehr, der Ladebalken dreht sich. Stop-Button drücken und prüfen, ob sich die while-Bedingung im Block wirklich ändert.

FAQ

Anfänger

Was bedeutet das überhaupt?

Warum heißt die Zählvariable so oft i? Das ist eine alte Tradition aus der Mathematik (index). Für einfache Zähler ist i in Ordnung. Wenn die Variable etwas Bestimmtes darstellt, nimm einen sprechenden Namen wie month oder player.

Wie oft darf eine Schleife laufen? Theoretisch unbegrenzt. Praktisch merkst du ab vielen Millionen Durchläufen, dass es dauert, besonders im Playground, der jede Zeile protokolliert.

Fortgeschritten

Warum verhält es sich so?

Was mache ich bei verschachtelten Schleifen, wenn ich beide beenden will? break beendet nur die innere Schleife. Mit einem Label kannst du gezielt die äußere beenden:

Swift
outer: for row in 1...5 {
    for column in 1...5 {
        if row * column == 6 {
            break outer
        }
    }
}

Brauchst du das oft, ist der Code meist zu verschachtelt. Dann hilft eine eigene Funktion (Lektion 1.10).

Gibt es Alternativen zu Schleifen? Ja. Für viele typische Aufgaben, etwa „alle Werte verdoppeln“ oder „nur bestimmte Werte behalten“, gibt es fertige Funktionen wie map und filter. Du lernst sie in Lektion 1.11 kennen. In SwiftUI selbst schreibst du übrigens fast nie for-Schleifen. Dort gibt es ForEach (Lektion 2.15).

Profi

Was passiert unter der Haube?

Wie funktioniert for-in unter der Haube? for-in funktioniert mit allem, was das Protokoll Sequence erfüllt. Swift holt sich davon einen Iterator und ruft so lange next() auf, bis nil zurückkommt. Bereiche, Strings, Arrays und Dictionaries erfüllen alle Sequence. Was Protokolle sind und was nil bedeutet, lernst du in den Lektionen 1.15 und 1.8.

Warum stürzt 5...1 ab, statt einfach nichts zu tun? Weil ein Bereich mit vertauschten Grenzen fast immer ein Logikfehler ist. Swift folgt dem Prinzip, solche Fehler früh und laut zu melden, statt stillschweigend weiterzumachen.

Übungen

Anfänger

Übung 1: Einmaleins

Gib die 7er-Reihe von 7 × 1 bis 7 × 10 aus, jeweils in der Form „7 × 3 = 21“.

Lösung Übung 1Erst selbst probieren!
Swift
for i in 1...10 {
    print("7 × \(i) = \(7 * i)")
}
Fortgeschritten

Übung 2: FizzBuzz

Der Klassiker unter den Programmieraufgaben: Gib die Zahlen von 1 bis 30 aus. Ist eine Zahl durch 3 teilbar, gib stattdessen „Fizz“ aus, ist sie durch 5 teilbar, „Buzz“, und ist sie durch beides teilbar, „FizzBuzz“.

Lösung Übung 2Erst selbst probieren!
Swift
for number in 1...30 {
    if number % 15 == 0 {
        print("FizzBuzz")
    } else if number % 3 == 0 {
        print("Fizz")
    } else if number % 5 == 0 {
        print("Buzz")
    } else {
        print(number)
    }
}

Der Fall „durch beides teilbar“ muss zuerst geprüft werden. Sonst würde bei 15 schon die Bedingung für 3 greifen. Erinnerst du dich an Lektion 1.4? Vom speziellsten zum allgemeinsten Fall.

Profi

Übung 3: Sparplan

Du legst 1.000 € zu 3 % Zinsen pro Jahr an. Die Zinsen werden jedes Jahr mitverzinst. Nach wie vielen Jahren hast du zum ersten Mal mehr als 2.000 €? Löse das mit einer while-Schleife.

Lösung Übung 3Erst selbst probieren!
Swift
var money = 1000.0
var years = 0

while money <= 2000 {
    money *= 1.03
    years += 1
}

print("Nach \(years) Jahren: \(money) €")
// Nach 24 Jahren: 2032.79… €

Zusammenfassung

  • for-in läuft über einen Bereich oder eine Sammlung von Werten.
  • _ statt eines Namens, wenn du den Wert nicht brauchst.
  • while wiederholt, solange eine Bedingung true ist. Vorsicht vor Endlosschleifen.
  • repeat-while läuft mindestens einmal.
  • continue überspringt einen Durchlauf, break beendet die Schleife.
  • Variablen, die über alle Durchläufe gelten sollen, gehören vor die Schleife.

Spickzettel

Swift
for i in 1...5 { ... }                     // 1 bis 5
for i in 0..<5 { ... }                     // 0 bis 4
for _ in 1...3 { ... }                     // 3-mal, ohne Zähler
for i in (1...5).reversed() { ... }        // rückwärts
for i in stride(from: 0, to: 10, by: 2) { ... }

while condition { ... }
repeat { ... } while condition

continue   // nächster Durchlauf
break      // Schleife beenden

Lektion geschafft?

Markiere sie als erledigt, damit du deinen Fortschritt siehst.