1. Der ternary operator
Wenn Sie gerade erst anfangen, Bedingungen zu schreiben, scheint if/else irgendwie alles abzudecken: wollten Sie pruefen, haben Sie geprueft; wollten Sie etwas ausgeben, haben Sie das eine ausgegeben; wollten Sie etwas anderes, eben das andere. Und das stimmt auch. Aber manchmal wollen wir nicht „einen Satz von Aktionen ausfuehren“, sondern schnell einen von zwei Werten auswaehlen und ihn einer Variable zuweisen, in einen String einsetzen oder weiterreichen. Genau dafuer gibt es den ternary operator ?: — wie eine kleine „Abzweigung mit einem Schritt“.
Wichtig ist, die Rolle des ternary operators sofort klar zu definieren: Er „ersetzt if“ nicht, sondern bietet einfach eine kuerzere Schreibweise fuer einen sehr konkreten Fall. Wenn Sie versuchen, damit alle Verzweigungen zu ersetzen, sieht der Code bald aus wie ein Geheimcode, und Sie wie jemand, der die Lesbarkeit auf null „optimiert“ hat.
Syntax condition ? valueIfTrue : valueIfFalse
Der ternary operator in Swift wird so gelesen: „Wenn die Bedingung wahr ist, nimm den ersten Wert, sonst den zweiten“. Und genau das ist ein Operator zur Wertauswahl. Er aehnelt einem Mini-if, nur eben in Form eines Ausdrucks (expression), dessen Ergebnis einer Variable zugewiesen werden kann.
Die Syntax sieht so aus:
condition ? valueIfTrue : valueIfFalse
?: in Swift
Die Bedingung (condition) muss vom Typ Bool sein. Man kann dort also keine Zahl „0/1“ einsetzen, wie es in manchen anderen Sprachen vorkommt. Swift ist an dieser Stelle streng und hat recht: Wenn Sie „ja/nein“ meinen, schreiben Sie „ja/nein“ und nicht „naja, so ziemlich ja“.
Ein Mini-Beispiel:
let age = Int(readLine() ?? "") ?? 0
let status = age >= 18 ? "adult" : "minor"
print(status) // bei Eingabe 20: adult
Hier ist age >= 18 ein Bool, und status bekommt einen von zwei Strings.
?: — ueber den Wert, nicht ueber Aktionen
Der haeufigste Fehler von Anfaengern ist, ?: als „kurzes if, um etwas zu tun“ zu verwenden. Besser ist es jedoch, ?: als „Auswahl des Ergebnisses“ zu verstehen, nicht als „Auswahl des Verhaltens“. Wenn Sie zwei unterschiedliche Aktionsfolgen ausfuehren muessen (zum Beispiel zwei print(), zwei Pruefungen, zwei Eingaben), dann ist das ein Fall fuer if/else — und man sollte sich das Leben nicht unnötig schwer machen.
Ein gutes Beispiel fuer die „Wertauswahl“:
let n = Int(readLine() ?? "") ?? 0
let parity = n % 2 == 0 ? "even" : "odd"
print("n is \(parity)") // bei Eingabe 7: n is odd
Ein Beispiel dafuer, wo der ternary operator schon verdächtig wirkt: wenn in den Zweigen ein „Mini-Szenario“ beginnt, besonders wenn dort mehrere Operationen stehen. Selbst wenn das technisch kompiliert, muss der Leser beide Zweige im Kopf „entpacken“, und dann gibt es keinen Vorteil an Kuerze mehr.
Beide Zweige muessen kompatible Typen haben
In if/else koennen Sie in einem Zweig print("Hi") und im anderen print("Bye") machen, und alles ist in Ordnung, weil if eine Kontrollstruktur fuer den Programmfluss ist und kein „Wertberechner“. Beim ternary operator ist das anders: Er muss einen Wert zurueckgeben, also muss Swift verstehen, welchen Typ dieser Wert hat.
Darum gilt die einfache Regel: Beide Zweige muessen denselben Typ haben (oder zumindest kompatibel sein), sonst sagt der Compiler: „Entschuldigung, ich bin kein Telepath.“
Arbeitsbeispiel (ueberall Strings):
let command = readLine() ?? ""
let response = command == "exit" ? "Bye!" : "Unknown command"
print(response)
Arbeitsbeispiel (ueberall Zahlen):
let a = Int(readLine() ?? "") ?? 0
let b = Int(readLine() ?? "") ?? 0
let maxValue = a > b ? a : b
print("max = \(maxValue)")
Und jetzt das typische „nein“:
// So darf man das nicht machen: in einem Zweig String, im anderen Int
let x = condition ? "text" : 10
Wenn Sie auf so etwas stossen, ist das meist ein Hinweis darauf, dass Sie entweder den falschen Ergebnistyp gewaehlt haben oder dass if/else und separate print()/Variablen besser passen.
2. Praktische Szenarien
?: innerhalb der Zeichenketteninterpolation
Einer der angenehmsten Einsatzbereiche von ?: ist das Erzeugen eines kurzen Textausschnitts innerhalb eines Strings. Wir wollen oft Dinge wie „Zugriff erlaubt/verweigert“, „gerade/ungerade“, „OK/FAIL“ schreiben, ohne den Code mit zusaetzlichen Variablen und Zweigen aufzublasen.
Zum Beispiel bauen wir einen kleinen „Status“ fuer einen Benutzer:
let score = Int(readLine() ?? "") ?? 0
print("Result: \(score >= 60 ? "PASS" : "FAIL")")
// bei Eingabe 75: Result: PASS
Das sieht normal aus, weil beide Zweige kurz sind und sich leicht lesen lassen. Hier funktioniert der ternary operator wie ein „einwortiger Einsatz“.
Noch ein Beispiel — eine saubere Meldung ueber einen Bereich:
let x = Int(readLine() ?? "") ?? 0
let label = (x >= 1 && x <= 10) ? "in range" : "out of range"
print("x is \(label)")
Klammern um die Bedingung sind nicht verpflichtend, koennen aber fuer die visuelle Gruppierung hilfreich sein, besonders wenn die Bedingung laenger ist als nur „eine Pruefung“.
Wann ?: sinnvoll ist und wann if/else besser ist
Wenn man lernt, moechte man einfache Regeln wie „Ternary ist immer schlecht“ oder „Ternary ist immer super“ haben. Aber die Realitaet ist langweiliger: Fast jedes Werkzeug wird schlecht, wenn man es wie einen Hammer benutzt, obwohl vor einem eine Schraube liegt.
Unten ist eine kompakte Orientierungstabelle:
| Situation | Was besser ist | Warum |
|---|---|---|
| Es soll ein kurzer Wert ausgewaehlt werden (Wort/Zahl) | |
Der Code wird kuerzer, der Sinn bleibt erhalten |
| In einem Zweig muessen mehrere Aktionen ausgefuehrt werden | |
Der ternary operator wird sonst zu einem Brei |
| Die Bedingung ist lang und komplex | meist oder in auslagern |
Lesbarkeit ist wichtiger als Knappheit |
| Die Zweige sind lang (mehrere Operationen/komplizierte Berechnungen) | |
Einfacher zu debuggen und zu lesen |
Man moechte in verschachteln |
fast immer |
Verschachtelung ist schlecht lesbar |
Diese Tabelle ist kein „Gesetz von Swift“, sondern gesunder Menschenverstand. Wenn Ihr Code schnell und eindeutig lesbar ist, sind Sie gut unterwegs. Wenn nicht, dann ist es Zeit zu vereinfachen.
Mini-Beispiel fuer eine CLI-Anwendung
Damit die Beispiele nicht nur einzelne „Fragmente“ sind, entwickeln wir den gedachten Rahmen unserer zukuenftigen CLI-Anwendung weiter. Noch ohne Schleifen, ohne Arrays und ohne Funktionen — nur ein Start, eine Eingabe, eine Reaktion. Stellen wir uns vor, es ist ein Prototyp von LibraryCLI, der spaeter mehr lernen wird, jetzt aber wenigstens gruessen und den Exit-Befehl verstehen kann.
print("Enter command:")
let command = readLine() ?? ""
let message = command == "exit" ? "Shutting down..." : "Unknown command: \(command)"
print(message)
Hier ist ?: sehr passend: Wir waehlen einen String fuer die Ausgabe. Keine Nebeneffekte, keine Tricks — nur Textauswahl.
Jetzt fuegen wir eine kleine „Validierung“ fuer leere Eingaben hinzu:
print("Enter command:")
let command = readLine() ?? ""
let normalized = command.isEmpty ? "<empty>" : command
print("You typed: \(normalized)")
Im Moment wirkt das wie eine Kleinigkeit, aber in echten CLI-Programmen machen solche Details die Ausgabe klarer und die Diagnose freundlicher. Und ja, der String "<empty>" ist keine Magie, sondern einfach ein praktisches Label.
Nur der ausgewaehlte Zweig wird ausgewertet
In den vorherigen Vorlesungen haben wir Short-Circuit bei && und || besprochen: Der rechte Teil wird vielleicht nicht ausgewertet. Der ternary operator hat eine aehnliche Idee: Es wird nur ein Zweig ausgewertet — der, den die Bedingung ausgewaehlt hat.
Das ist nuetzlich, wenn einer der Zweige potenziell „gefaehrlich“ ist (zum Beispiel eine Division durch Null ausloesen koennte), Sie ihn aber ueberhaupt nicht anfassen wollen, wenn die Bedingung nicht passt.
Ein kleines Beispiel mit Division:
let a = Int(readLine() ?? "") ?? 0
let b = Int(readLine() ?? "") ?? 0
let resultText = (b == 0) ? "division by zero" : "a/b = \(a / b)"
print(resultText)
Wenn b == 0 ist, wird der rechte Zweig mit a / b nicht ausgewertet, und das Programm stuerzt nicht ab. Der ternary operator ist hier wie ein „hoeflicher Pförtner am Eingang“.
3. Lesbarkeit und Stil
Ein verschachtelter ?: verschlechtert fast immer das Verstaendnis
Irgendwann versucht garantiert jemand, „schoen in einer Zeile“ zu schreiben: drei Antwortmoeglichkeiten, also zwei ?: hintereinander. Technisch erlaubt Swift das, aber beim Lesen ist es oft eine Katastrophe: Das Gehirn muss mehrere Bedingungen und Zweige gleichzeitig im Kopf behalten, und ein Fehler bei den Klammern wird zu einem Bug, der logisch aussieht, aber etwas anderes tut.
Nehmen wir die Aufgabe: Fuer score soll "A", "B" oder "C" zurueckgegeben werden. Ein verschachtelter ?: ist moeglich:
let score = Int(readLine() ?? "") ?? 0
let grade = score >= 90 ? "A" : (score >= 70 ? "B" : "C")
print(grade)
Das kompiliert, liest sich aber schlechter als eine normale „Leiter“:
let score = Int(readLine() ?? "") ?? 0
let grade: String
if score >= 90 {
grade = "A"
} else if score >= 70 {
grade = "B"
} else {
grade = "C"
}
print(grade)
Hier gewinnt if/else if/else, weil es hier nicht mehr um die Auswahl eines Werts aus zwei Optionen geht, sondern um eine Klassifizierung nach mehreren Bereichen. Man kann den ternary operator dafuer zwar missbrauchen, aber fuer den Leser ist das unangenehm.
Operatorprioritaet und Klammern
„Kompiliert“ bedeutet nicht „hat genau den Sinn, den Sie wollten“. Auch der ternary operator hat eine Prioritaet und kann sich mit &&, ||, +, - usw. mischen. Wir muessen die Prioritaetstabelle jetzt nicht auswendig lernen, aber eine praktische Regel sollte man sich merken: Wenn ein Ausdruck visuell mehrdeutig wird, setzen Sie Klammern oder teilen Sie ihn in Zwischen-lets auf.
Statt „Heldentum in einer Zeile“ lieber so:
let age = Int(readLine() ?? "") ?? 0
let isAdult = age >= 18
let label = isAdult ? "adult" : "minor"
print("status: \(label)")
So gibt es keine Fragen zur Prioritaet, und das Debugging ist einfacher: Sie koennen isAdult separat ausgeben, falls etwas schiefgeht.
Mikro-Refactoring: erst Bool, dann ?:
Oft wird Code besser, wenn Sie zuerst der Pruefung einen Namen geben und erst danach den Wert waehlen. Das ist besonders fuer Anfaenger nuetzlich: Man lernt, nicht als „Brei aus Operatoren“, sondern in kleinen, verstandlichen Schritten zu denken.
Vergleichen wir zwei Varianten.
Variante „alles in einer Zeile“:
let n = Int(readLine() ?? "") ?? 0
print(n % 2 == 0 ? "even" : "odd")
Variante „mit Namen fuer den Sinn“:
let n = Int(readLine() ?? "") ?? 0
let isEven = n % 2 == 0
let text = isEven ? "even" : "odd"
print(text)
Die zweite Variante ist laenger, erklaert sich aber besser von selbst. Und wenn Sie den Code zwei Wochen spaeter lesen, sagen Sie sich „Danke“ — ein seltener Moment der Harmonie zwischen Ihnen und Ihrem frueheren Ich.
4. Typische Fehler bei der Verwendung von ?:
Fehler Nr. 1: Versuch, mit dem ternary operator ein vollwertiges if/else mit Aktionen zu ersetzen.
Wenn in den Zweigen mehrere Codezeilen, mehrere print() oder irgendeine Datenvorbereitung auftauchen, ist der ternary operator kein „kurzer Wertauswahlmechanismus“ mehr, sondern ein Trick um des Tricks willen. In solchen Situationen ist if/else einfacher zu lesen und schneller zu debuggen, weil jeder Zweig ein normaler Block { ... } ist.
Fehler Nr. 2: Zweige mit unterschiedlichen Typen.
Anfaenger wundern sich oft, warum man nicht schreiben kann: „wenn true — String, sonst — Zahl“. Der Grund ist, dass ?: einen einzigen Wert von einem einzigen Typ zurueckgeben muss. Wenn Sie auf dieses Problem stossen, sollten Sie entweder beide Zweige auf denselben Typ bringen (zum Beispiel beide zu Strings machen) oder die Logik neu entwerfen: Vielleicht brauchen Sie if/else und unterschiedliche Aktionen.
Fehler Nr. 3: Verschachtelte ?: fuer „Schoenheit in einer Zeile“.
Ein verschachtelter ternary operator wird schnell unlesbar: Sie zwingen den Leser, mehrere Bedingungen im Kopf zu behalten und auf Klammern zu achten. Selbst wenn Sie sicher sind, dass „das doch klar ist“, wird es spaeter auch fuer Sie unklar sein. Fuer drei und mehr Varianten ist fast immer if/else if/else besser.
Fehler Nr. 4: Eine zu lange Bedingung direkt in ?:.
Wenn die Bedingung mehrere Vergleiche, &&, ||, Negationen ! und Klammern enthaelt, steigt die Gefahr, dass der Leser sie falsch versteht. In solchen Faellen ist der Ansatz besser, die Bedingung in let is... auszulagern und den ternary operator nur fuer die finale Wertauswahl zu lassen.
Fehler Nr. 5: ?: dort zu verwenden, wo es wichtiger ist, den Sinn zu erklaeren als Code zu sparen.
Der ternary operator ist gut, wenn er den Code kuerzer macht, ohne Klarheit zu verlieren. Wenn aber der Sinn der Bedingung nicht offensichtlich ist oder die Zweige wichtige Business-Logik tragen, sollte man lieber 3–5 Zeilen mehr schreiben und den Code „wie Text lesbar“ machen. Swift mag Lesbarkeit ohnehin, also ist das nicht nur „schoen“, sondern auch idiomatisch richtig.
GO TO FULL VERSION