Swift — Besondere Operatoren

Swift kennt im Wesentlichen dieselben Operatoren wie die meisten anderen Programmiersprachen — also etwa +, -, * und / für die Grundrechnungsarten, = für Zuweisungen, == und === für Vergleiche etc. Allerdings gibt es in Swift auch einige Besonderheiten, die einen genaueren Blick rechtfertigen. Vorweg ein Überblick:

Operator         Bedeutung
------------     -----------------------------
(a, b) = (1, 2)  Mehrfachzuweisung
a &+ b           Addition ohne Überlaufkontrolle
a &- b           Subtraktion ohne Überlaufkontrolle
a &* b           Multiplikation ohne Überlaufkontrolle
a &/ b           Division ohne Überlaufkontrolle
a &% b           Restwert ohne Überlaufkontrolle
a ?? b           Nil Coalescing
a ~= b           Vergleichsoperator für `switch/case`

Mehrfachzuweisungen

Die Syntax a=b=3 als Kurzschreibweise von a=3 und b=3 ist in Swift nicht zulässig. Dafür können mehrere Variablen en bloc in einer Tupel-Schreibweise zugewiesen werden. Das ist auch praktisch für Funktionen, die nicht einen Wert zurückgeben soll, sondern mehrere Werte.

var (a, b, c) = (1, 7, 12)

Kein automatisches Casting

Alle Operatoren setzen voraus, dass links und rechts von ihnen jeweils gleichartige Datentypen verwendet werden!

var a = 3       // a ist eine Integer-Variable
var b = 1.7     // b ist ein Fließkommavariable
var c = a + b   // Fehler, Int-Wert + Double-Wert nicht zulässig

Wenn Sie die Summe von a plus b ausrechnen möchten, müssen Sie explizit den Datentyp einer der beiden Operatoren anpassen. Int rundet dabei immer ab, d.h. aus 1.7 wird 1.

var c1 = a + Int(b)     // c1 = 4
var c2 = Double(a) + b  // c2 = 4.7

Rechnen ohne Überlaufkontrolle

Swift führt (anders als beispielsweise Java!) standardmäßig bei bei allen Grundrechenarten eine Überlaufkontrolle durch. Wenn Sie sicher sind, dass kein Überlauf stattfinden kann, und Sie besonders effiziente Algorithmen programmieren möchten, verwenden Sie stattdessen die Operatoren &+, &-, &*, &/ und &%: Sie führen die Grundrechenarten ohne Überlaufkontrolle durch. Sollte allerdings doch ein Überlauf eintreten, dann ist das Ergebnis falsch!

var i = 10000000          // Integer
var result = i &* i &* i  // falsches Ergebnis 3.875.820.019.684.212.736

Der Nil-Coalescing-Operator

Gewöhnliche Variablen dürfen in Swift nie null sein. Das ist nur speziell gekennzeichneten Variablen vorbehalten, den sogenannten Optionals. Dabei folgt dem Datentyp ein Fragezeichen oder ein Ausrufezeichen. Diese Variablen können zwar auch nicht null sein, aber sie dürfen den Swift-spezifischen Zustand nil annehmen.

var x:Int? = 3     // x enthält eine ganze Zahl oder nil
var y:Int! = 4     // y enthält eine ganze Zahl oder nil
var z:Int  = 5     // z enthält immer eine ganze Zahl
x = nil            // ok
y = nil            // ok
z = nil            // nicht erlaubt

Der Unterschied zwischen x und y besteht darin, dass das Auspacken (Unwrapping) des eigentlichen Werts bei y automatisch erfolgt, während es bei x durch ein nachgestelltes Ausrufezeichen erzwungen werden muss. (Zu den weiteren Besonderheiten von Optionals plane ich demnächst einen eigenen Blog-Beitrag.)

Mit diesem Vorwissen kommen wir nun zum ebensoschwer aussprechlichen wie übersetzbaren Nil-Coalescing-Operator a ?? b. Dabei handelt es sich um eine Kurzschreibweise des folgenden Ausdrucks:

a != nil ? a! : b

Wenn a initialisiert ist, also nicht nil ist, dann liefert a ?? b den Wert von a zurück, andernfalls den Wert von b. Damit eignet sich b zur Angabe eines Defaultwerts. Das Ausrufezeichen in a! bewirkt das Auspacken (Unwrapping) des Optionals.

var i = x ?? -1       // der Datentyp von i ist Int

Vergleichsoperator für switch/case

Swift kennt mit ~= einen Vergleichsoperator, der intern in switch-case-Konstruktionen verwendet wird. Er bietet nur wenige Funktionen:

  • Zwei Ausdrücke des gleichen Typs werden wie mit == verglichen.

  • Außerdem kann ein durch den Range-Operator formulierters Intervall mit einer ganzen Zahl verglichen werden. -2...2 ~= 1 ist true, 1...10 ~= 12 liefert false. Achten Sie darauf, dass Sie zuerst den Bereich und dann den Vergleichswert angeben müssen. Wenn Sie die Reihenfolge vertauschen, funktioniert der Operator nicht.

1...10 ~= 8        // true
1.7..<2.9 ~= 2.3   // true
"a"..."z" ~= "f"   // true
"0"..."9" ~= "x"   // false

Hinter den Kulissen kommt ~= in switch-Ausdrücken zum Einsatz, wo mit case überprüft werden kann, ob sich ein ganzzahliger Ausdruck in einem vorgegebenen Bereich befindet:

let n = 12
switch n {
case (1...10):
  println("Zahl zwischen 1 und 10")
case(11...20):
  println("Zahl zwischen 11 und 20")
default:
  println("Andere Zahl")
}

Range-Operatoren

In Swift gibt es zwei Operatoren, um Bereiche ganzer Zahlen auszudrücken:

  • Der Closed-Range-Operator a...b beschreibt einen Bereich von a bis inklusive b.

  • Der Half-Open-Range-Operator a..<b beschreibt hingegen einen Bereich von a bis exklusive b, entspricht also a...b-1.

Mit den Range-Operatoren definierte Bereiche können in Schleifen, in switch-case-Ausdrücken und mit dem vorhin vorgestellten Operator ~= verarbeitet werden.

for i in 1...10 {
    println(i)
}

In der folgenden Schreibweise können Sie Zahlenbereiche zur Initialisierung eines Integer-Arrays verwenden:

var ar = [Int](1...10)  // entspricht var ar = [1, 2, ..., 10]

Mit map können Sie auf einen Zahlenbereich direkt eine Funktion (Closure) anwenden:

(1...10).map { println($0) }

Bereiche (Ranges) können können nicht nur für Integer-Zahlen gebildet werden, sondern auch für andere Datentypen, die dem Protokoll ForwardIndexType entsprechen. Dazu zählen unter anderem auch String.Index-Elemente zur Positionsangabe in Zeichenketten. Double-Zahlen sind hingegen nicht geeignet, weil Sie das weder das ForwardIndexType-Protokoll noch dessen successor-Methode unterstützen.

Je nach Kontext können die Operatoren a...b und a..<b aber auch zur Formulierung eines Intervalls verwendet werden — z.B. als Vergleichsbasis für switch bzw. für den oben beschriebenen Operator ~=. In diesem Fall sind für a und b auch Fließkommazahlen oder einzelne Zeichen (Character) erlaubt.

Operator Overloading, Definition neuer Operatoren

Swift ermöglicht es, vorhandene Operatoren durch eigene Implementierungen zu ersetzen sowie neue Operatoren zu definieren. Das ist vor allem dann zweckmäßig, wenn Sie zuerst eine eigene Klasse entwickeln und dann Objekte dieser Klasse vergleichen, aneinanderfügen etc. wollen.

struct Complex {
    var re:Double, im:Double
    init(re:Double, im:Double) {
        self.re=re; self.im=im
    }
}

// Multiplikation komplexer Zahlen
func * (left: Complex, right: Complex) -> Complex {
    return Complex(re:left.re*right.re - left.im * right.im,
        im:left.re*right.im+left.im*right.re)
}

var a = Complex(re: 2, im: 1)  //  2 +  i
var b = Complex(re: 1, im: 3)  //  1 + 3i
var c = a * b                  //  3 + 4i

Links / Quellen