Smart Technologies, Apps, Software Development

Swift-Tutorial Teil 4: Die vier Grundrechenarten

Wir haben im letzten Blog gelernt, wie in Swift Zeichenketten umgesetzt werden und wie wir damit arbeiten können. Am Ende des Artikels haben wir ein kleines Programm geschrieben, dass eine einfache Summe selbständig ausrechnen kann. In diesem Blog werden wir lernen, wie wir das Progrämmchen etwas komfortabler gestalten können und es gleichzeitig so erweitern, dass es die vier Grundrechenarten Addition, Subtraktion, Multiplikation und Division beherrscht.

rakete-smarthone-app-orange


Zur Erinnerung, hier nochmals die Funktion aus Blogartikel 3, die eine Addition durchführt:

func compute(addition : String) -> Double {
    let plusIndex      = addition.index(of: "+")!
    let leftUntrimmed  = addition[..<plusIndex]
    let leftStr        = leftUntrimmed.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
    let left           = Double(leftStr)!
    let rightUntrimmed = addition[addition.index(after: plusIndex)..<addition.endIndex]
    let rightStr       = rightUntrimmed.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
    let right          = Double(rightStr)!
    
    return add(value1: left , value2: right)
}

func add(value1: Double, value2: Double) -> Double {
    return value1 + value2
}

let expression = "20 + 7"
let result = compute(addition: expression)
print("Die Summe \(expression) ist gleich \(result)")

 
Falls dir die Funktionsweise dieser Funktion nicht mehr klar ist, solltest du dir zunächst Blog 3 noch einmal anschauen. Ansonsten möchte ich jetzt die Gelegenheit nutzen und dich auffordern, folgende Themen durchzulesen bevor es weitergeht: Swifts Operatoren, Flow Control und Optionals. Keine Angst, die Themen sind nicht besonders kompliziert. Danach sehen wir uns wieder hier.

Swifts Operatoren

Ein Operator ist ein spezielles Symbol oder eine Kombination von speziellen Symbolen, die du dazu nutzen kannst, Werte zu prüfen, zu ändern oder zu kombinieren. Zum Beispiel addiert der Operator + zwei Zahlen miteinander, wie in let i = 1 + 2, wo i der Wert 3 zugewiesen wird.

Im Folgenden werden die wichtigsten Operatoren beschrieben.

Zuweisungsoperator

Der Zuweisungsoperator = weist einer Konstanten oder Variablen einen Wert zu. Betrachte dazu das nächste Beispiel:

let b = 10
var a = 5

print("a ist gleich \(a)")

a = b

print("a ist gleich \(a)")

Nach den ersten zwei Zeilen hat die Variable a den Wert 5 und die Konstante b den Wert 10. Mit der Zuweisung a = b wird der Variablen a der Wert der Variablen b zugewiesen, so dass a fortan den Wert 10 besitzt.

Während auf der linken Seite einer Zuweisung nur eine Variable oder eine Konstante stehen darf, kann die rechte Seite jeden Ausdruck enthalten, der einen Wert zurück liefert, der der links stehenden Variable oder Konstanten zugewiesen werden darf. Hier ein paar Beispiele:

var name = "Peter Pan"
var abstand = berechneAbstand(x: 10, y: 332)
var groesse = 35 - 3 - 5 


Da die hier gezeigten Variablen mit keinem expliziten Typ definiert worden sind, haben sie automatisch den Typ, des zugewiesenen Wertes. name ist somit vom Typ String, abstand vom Typ Int (wir nehmen hier einfach an, dass die Funktion berechneAbstand ein Int zurückliefert) und groesse ebenfalls Int.

Arithmetische Operatoren

Swift unterstützt die vier wichtigsten arithmetische Operatoren Addition (+), Subtraktion (-), Multiplikation (*) und Division (/) für alle Zahlentypen. Hier ein Beispiel:

let c : Int    = 13 + 14
let d : Int    = 36 - 9
let e : Double = 13.5 * 2
let f : Double = 54.0 / 3.0

Der Additionsoperator + kann zusätzlich dazu benutzt werden, um Zeichenketten aneinanderzuhängen, wie im folgendem Beispiel:

let gruss = "Hallo" + ", " + "Peter!"

  

Vergleichsoperatoren

Vergleichsoperatoren sind Operatoren, die Werte miteinander vergleichen und als Ergebnis wahr (true) oder falsch (false) zurückliefern. Wie im ersten Blog beschrieben, ist in Swift Bool der Typ, der boolsche Werte repräsentiert. Betrachte folgenden Sourcecode, in dem alle existierenden Vergleichsoperatoren beschrieben sind.

var a = 1
var b = 2

var isEqual          : Bool = a == b
var isNotEqual       : Bool = a != b
var isGreater        : Bool = a > b
var isLess           : Bool = a < b
var isGreaterOrEqual : Bool = a >= b
var isLessOrEqual    : Bool = a <= b

print(isEqual)
print(isNotEqual)
print(isGreater)
print(isLess)
print(isGreaterOrEqual)
print(isLessOrEqual)

Flow Control

Swift bietet eine Vielzahl von Kontrollfluss-Anweisungen an. Dazu gehören while-Schleifen, um eine Aufgabe mehrmals auszuführen; if-, guard- und switch-Anweisungen, um verschiedene Zweige des Codes in Abhängigkeit bestimmter Bedingungen auszuführen; und Anweisungen wie break und continue, um die Ausführung des Sourcecodes auf eine andere Stelle im Sourcecode springen zu lassen. In diesem Blog werden wir die klassische if-Anweisung kennenlernen, aber auch eine besondere Erweiterung daraus, die sich Optional Binding nennt.

 

Die if-Anweisung

Es ist oft sinnvoll, Teile des Sourceodes unter bestimmten Bedingungen auszuführen. Möglicherweise möchtest du einen speziellen Sourcecode nur dann ausführen, wenn ein Fehler auftritt, oder eine Meldung anzeigen, wenn ein Wert zu hoch oder zu niedrig wird. Um dies zu erreichen, kannst Du die Ausführung von Sourcecode mithilfe der if-Anweisung an eine Bedingung knüpfen. Betrachte folgendes Beispiel:

var temperatur = 25

if (temperatur <= 30) {
    print("Hey, es ist zu kalt!")
}


In dieser simplen if-Anweisung wird überprüft, ob der Wert der Variablen gleich oder kleiner als 30 ist. Wenn das der Fall ist (und hier ist es ja so), soll der Sourcecode zwischen den geschweiften Klammern ausgeführt werden. In diesem Fall wird der Satz “Hey, es ist zu kalt!” ausgegeben. Wenn jedoch der Wert der Variablen größer als 30 ist, wird das Programm hinter der schließenden Klammer fortgeführt.

Die erweiterte Form einer if-Anweisung bietet die Möglichkeit, auch für denn Fall, dass die Bedingung nicht erfüllt wird, einen speziellen Sourcecode auszuführen.

var temperatur = 25

if (temperatur == 30) {
    print("Hey, es ist zu kalt!")
} else {
    print("Endlich gutes Wetter!")
}


Hier wird in dem Fall, dass die Bedingung nicht erfüllt ist, der Sourcecode ausgeführt, der hinter dem else zwischen den geschweiften Klammern enthalten ist. 

Optionals

Hin und wieder gibt es die Situation, dass eine Funktion nicht in jedem Fall einen Wert zurückliefern kann. Ein Beispiel für solch einen Fall ist der Initilizer Double(String). Zur Erinnerung: Double(String) ist eine spezielle Funktion (Initializer), die ein Double mit dem Wert des übergebenen Strings als Zahl zurückliefert. Das bedeutet, dass Double(“1”) für uns die Verwandlung des übergebenen Strings in ein Double durchführt. In Blog 3 hatten wir diesen Initializer kennengelernt.

Wenn ich nun fälschlicherweise eine Zeichenkette übergebe, die keine Zahl darstellt, sagen wir “Kaninchenstall”, kann mir die Funktion Double(“Kaninchenstall”) natürlich keine Zahl zurückliefern. Was tut man in solchen Fällen? Wäre es nicht schön, wenn eine Funktion in solchen Fällen schlicht und einfach keinen Wert zurückliefert? Wie würde die Rückgabe dann konkret aussehen?

Nun, Swift kennt das Konzept eines sogenannten Optionals, das anstelle des eigentlich erwarteten Wertes zurückgeliefert wird. Betrachte folgendes Beispiel:

let zahl : Double = Double("1")


Wenn du den obigen Sourcecode eingibst, wird Xcode dir folgenden Fehler anzeigen: Value of optional type ‚Double?‘ not unwrapped; did you mean to use '!' or '?'?

Um diese Fehlermeldung zu verstehen, muss du zunächst genau wissen, was ein Optional ist. Da der Initializer Double(string) möglicherweise fehlschlägt, gibt er ein optionales Double zurück, anstatt ein Double. Ein optionales Double wird als Double? geschrieben, nicht Double. Das Fragezeichen zeigt an, dass der darin enthaltene Wert optional ist, d. h. dass er einen Double-Wert oder gar keinen Wert enthalten kann.

Im obigen Beispiel ist die Konstante zahl explizit als Double deklariert. Der Initializer liefert aber ein optionales Double, also ein Double? zurück. Und da Swift eine typsichere Programmiersprache ist, führt dies zu einem Fehler.

Um diesen Fehler zu korrigieren, gibt es zwei Lösungswege. Die erste Möglichkeit, den Fehler los zu werden, besteht darin, den Typ der Konstante von Double auf Double? zu verändern, sodass der Typ deklariert wird, der auch vom Initializer zurückgeliefert wird. Das obere Beispiel kannst Du folgendermaßen ändern:

let zahl : Double? = Double("1")


Diese Art der Deklaration und Definition führt dazu, dass die Konstante zahl den Wert 1 erhält. Und was passiert, wenn wir nicht “1” übergeben, sondern “Kaninchenstall”?

let zahl : Double? = Double("Kaninchenstall")


Auch hier gibt es keinen Kompilierfehler. Dafür hat die Konstante zahl jetzt den Wert nil. nil ist ein Wert der typlos ist und die Abwesenheit eines validen Wertes repräsentiert. Du kannst nil nicht mit nicht-optionalen Konstanten und Variablen verwenden. Wenn eine Konstante oder Variable in deinem Sourcecode unter bestimmten Bedingungen mit dem Fehlen eines Wertes arbeiten muss, deklariere ihn immer als optionalen Wert des entsprechenden Typs.

Doch nun zum zweiten Lösungsweg, um den Kompilierfehler zu beseitigen. Du musst dir darüber im Klaren sein (falls nicht schon geschehen), dass ein Optional nicht den erwartete Wert darstellt, sondern so etwas ist, wie einen Briefumschlag, in dem der eigentliche Wert enthalten ist.

Optional-Wert-Swift  

Das bedeutet im Umkehrschluss, dass du immer den Wert aus dem Optional herausnehmen musst, bevor du den Wert verwendest. Betrachte dazu folgendes Beispiel:

let zahl1 : Double? = Double("1")
let zahl2 : Double? = Double("2")

print(zahl1 + zahl2)


Der Compiler wir dir hier einen Fehler anzeigen: Binary operator '+' cannot be applied to two 'Double?' operands. Dieser Fehler sagt aus, dass der Operator + nicht auf Optionals angewendet werden darf, denn genau das enthalten die zwei Konstanten zahl1 und zahl2. Eigentlich logisch, zudem wollen wir ja nicht zwei Optionals addieren, sondern zwei Zahlen. Um an den eigentlichen Wert heran zu kommen, muss man das Zeichen ! nutzen. Das geschieht dann folgendermaßen:

print(zahl1! + zahl2!)


Das Ausrufezeichen, gesetzt hinter einer bereits initialisierten Variablen oder Konstanten, bewirkt das Auspacken (unwrapping) des Wertes. Man kann aber auch einen Wert, der zugewiesen wird, kurz vorher auspacken, wie im nächsten Beispiel:

let zahl1 : Double = Double("1")!
let zahl2 : Double = Double("2")!

print(zahl1 + zahl2)


Hier wird der zurückgelieferte Wert von Double(“1”) zuerst ausgepackt und dann der Variablen zahl1 zugewiesen. Deswegen muss hier auch der Typ Double sein und nicht Double?.

So weit, so gut! Wir wissen jetzt, was ein Optional ist und wie es verwendet werden kann. Doch beachte noch eins: Der Versuch, mit ! auf einen nicht vorhandenen optionalen Wert zuzugreifen, löst einen Laufzeitfehler aus. Stelle bitte immer sicher, dass ein optionaler Wert einen Wert enthält, bevor Du ! verwenden, um seinen Wert auszupacken.

Als letztes werde ich dir erklären, wie man eine Funktion programmiert, die einen optionalen Wert zurückliefert.

Angenommen wir möchten eine Funktion implementieren, die eine Zeichenkette in eine Zahl umwandelt, und nennen diese Funktion kurzerhand convertToInt(value : String). Die Implementierung dieser Funktion ist zwar trivial, da wir bereits wissen, dass der Initializer Int(String) genau diese Umwandlung durchführt, aber es geht ja nur darum, zu zeigen, wie ein Optional als Rückgabewert deklariert wird. Betrachte folgenden Sourcecode:

func convertToInt(value : String) -> Int? {
    return Int(value)
}


Wie du siehst, wird einfach als Rückgabewert nicht Int, sondern Int? deklariert. Und da der Initializer genau das zurückliefert, sind wir fein raus.

Optional Binding

Mit Hilfe des Optional Bindings kannst du herausfinden, ob ein Optional einen Wert enthält, und wenn ja, diesen Wert als temporäre Konstante oder Variable zur Verfügung stellen. Ein Optional Binding kann mit if-Anweisungen verwendet werden. Am Besten wir schauen uns ein Beispiel an:

func convertToInt(value : String) -> Int? {
    return Int(value)
}

Wie du sehen kannst, wird direkt hinter dem if das Schlüsselwort let geschrieben, womit die Konstante a deklariert und mit Int(zahl) initialisiert wird. Anders gesagt, zuerst wird der Konstanten a ein Wert zugewiesen und danach wird überprüft, ob der übergebene Wert nil ist oder nicht. Wenn a einen gültigen Wert bekommt, ist automatisch die Konstante a innerhalb der geschweiften Klammern vor dem else definiert. Im oberen Beispiel wird daher der Text “Die Zahl ist 1” ausgegeben.

Du kannst gerne den der Variable zahl zugewiesenen Wert auf “Hallo” ändern. In diesem Falle wir der else Zweig ausgeführt.


So, da sind wir wieder. Ich hoffe die Theorie war nicht zu trocken. Okay, dann legen wir los. Doch bevor wir die neu gelernten Eigenschaften der Programmiersprache Swift anwenden, möchte ich auf Basis der Erkenntnisse der letzten drei Blogartikel die Implementierung der vier arithmetischen Operatoren vorstellen. Hier der Sourcecode:

func compute(addition : String) -> Double {
    let plusIndex      = addition.index(of: "+")!
    let leftUntrimmed  = addition[..<plusIndex]
    let leftStr        = leftUntrimmed.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
    let left           = Double(leftStr)!
    let rightUntrimmed = addition[addition.index(after: plusIndex)..<addition.endIndex]
    let rightStr       = rightUntrimmed.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
    let right          = Double(rightStr)!

    return add(value1: left , value2: right)
}

func compute(subtraction : String) -> Double {
    let minusIndex     = subtraction.index(of: "-")!
    let leftUntrimmed  = subtraction[..<minusIndex]
    let leftStr        = leftUntrimmed.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
    let left           = Double(leftStr)!
    let rightUntrimmed = subtraction[subtraction.index(after: minusIndex)..<subtraction.endIndex]
    let rightStr       = rightUntrimmed.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
    let right          = Double(rightStr)!

    return subtract(value1: left , value2: right)
}

func compute(multiplication : String) -> Double {
    let timesIndex     = multiplication.index(of: "*")!
    let leftUntrimmed  = multiplication[..<timesIndex]
    let leftStr        = leftUntrimmed.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
    let left           = Double(leftStr)!
    let rightUntrimmed = multiplication[multiplication.index(after: timesIndex)..<multiplication.endIndex]
    let rightStr       = rightUntrimmed.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
    let right          = Double(rightStr)!

    return multiply(value1: left , value2: right)
}

func compute(division : String) -> Double {
    let byIndex        = division.index(of: "/")!
    let leftUntrimmed  = division[..<byIndex]
    let leftStr        = leftUntrimmed.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
    let left           = Double(leftStr)!
    let rightUntrimmed = division[division.index(after: byIndex)..<division.endIndex]
    let rightStr       = rightUntrimmed.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
    let right          = Double(rightStr)!

    return divide(value1: left , value2: right)
}


func add(value1: Double, value2: Double) -> Double {
    return value1 + value2
}

func subtract(value1: Double, value2: Double) -> Double {
    return value1 - value2
}

func multiply(value1: Double, value2: Double) -> Double {
    return value1 * value2
}

func divide(value1: Double, value2: Double) -> Double {
    return value1 / value2
}

let myAddition = "20 + 7"
let mySubtraction = "35 - 8"
let myMultiplication = "13.5 * 2"
let myDivision = "54 / 2"

let additionResult = compute(addition: myAddition)
let subtractionResult = compute(subtraction: mySubtraction)
let multiplicationResult = compute(multiplication: myMultiplication)
let divisionResult = compute(division: myDivision)

print("Die Summe \(myAddition) ist gleich \(additionResult)")
print("Die Subtraktion \(mySubtraction) ist gleich \(subtractionResult)")
print("Die Multiplikation \(myMultiplication) ist gleich \(multiplicationResult)")
print("Die Subtraktion \(myDivision) ist gleich \(divisionResult)")


Wie du siehst sind die Implementierungen der restlichen Funktionen fast identisch. Sie unterscheiden sich lediglich in der ersten Zeile, in der nach einem spezifischen Operator gesucht, und in der letzten Zeile, in der die berechnende Funktion aufgerufen wird. Ich denke, die vier Funktionen mit Namen compute schreien förmlich nach Optimierung. Okay, dann lass uns das neue Wissen anwenden. Betrachte dazu folgende alternative Implementierung:

func compute(expression : String) -> Double {
    var operatorIndex  = expression.index(of: "+")

    if (operatorIndex == nil) {
        operatorIndex  = expression.index(of: "-")

        if (operatorIndex == nil) {
            operatorIndex  = expression.index(of: "*")

            if (operatorIndex == nil) {
                operatorIndex  = expression.index(of: "/")
            }
        }
    }
    
    let operatorSymbol = expression[operatorIndex!]
    let leftUntrimmed  = expression[..<operatorIndex!]
    let leftStr        = leftUntrimmed.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
    let left           = Double(leftStr)!
    let rightUntrimmed = expression[expression.index(after: operatorIndex!)..<expression.endIndex]
    let rightStr       = rightUntrimmed.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
    let right          = Double(rightStr)!


    if (operatorSymbol == "+") {
        return add(value1: left , value2: right)
    }

    if (operatorSymbol == "-") {
        return subtract(value1: left , value2: right)
    }

    if (operatorSymbol == "*") {
        return multiply(value1: left , value2: right)
    }

    return divide(value1: left , value2: right)
}


Wir haben die vier Funktionen mit dem Namen compute auf eine reduziert. Diese Implementierung nutzt if-Anweisungen, um zwei Dinge zu erreichen:

  1. Zuerst wird der Index (Position) des Operators ermittelt und in der Variablen operatorIndex gespeichert – und das unabhängig davon, um welchen Operator es sich handelt. Für den darauf folgenden Block an Sourcecode ist der tatsächliche Operator (“+”, “-”, “*” oder “/“) nicht notwendig. Lediglich die Position, die durch operatorIndex repräsentiert wird, ist wichtig. Nach dem mittleren Block stehen der rechte und der linke Operand fest.
  2. Am Ende wird wieder mithilfe von if-Anweisungen, je nach gewünschter Operation, die entsprechende Funktion aufgerufen, die schließlich die Berechnung durchführt.

Das ist ja schon mal etwas. Aber noch nicht genug. An dieser Stelle möchte ich dir weitere Optimierungen zeigen. Lass uns zunächst die ersten vier if-Anweisungen auslagern. Dazu schaue dir folgenden Sourcecode an:

func compute(expression : String) -> Double {
    let operatorIndex  = getOperatorIndex(expression)
    let operatorSymbol = expression[operatorIndex!]
    let leftUntrimmed  = expression[..<operatorIndex!]
    let leftStr        = leftUntrimmed.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
    let left           = Double(leftStr)!
    let rightUntrimmed = expression[expression.index(after: operatorIndex!)..<expression.endIndex]
    let rightStr       = rightUntrimmed.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
    let right          = Double(rightStr)!
    
    
    if (operatorSymbol == "+") {
        return add(value1: left , value2: right)
    }
    
    if (operatorSymbol == "-") {
        return subtract(value1: left , value2: right)
    }
    
    if (operatorSymbol == "*") {
        return multiply(value1: left , value2: right)
    }
    
    return divide(value1: left , value2: right)
}

func getOperatorIndex(_ expression : String) -> String.Index? {
    var index  = expression.index(of: "+")
    
    if (index == nil) {
        index  = expression.index(of: "-")
        
        if (index == nil) {
            index  = expression.index(of: "*")
            
            if (index == nil) {
                index  = expression.index(of: "/")
            }
        }
    }
    
    return index
}


Wie du sehen kannst, habe ich die Funktion getOperatorIndex(_expression : String) eingeführt, um die oberen if-Anweisungen auszulagern. Beachte bitte, dass der Rückgabetyp String.Index? optional ist. Der Grund dafür ist, dass die zurückgelieferte Variable index optional ist. Und die wiederum ist deswegen optional, weil der ihr zugewiesene Ausdruck expression.index(of:) genau diesen optionalen Rückgabetyp String.Index? besitzt. Das alles zusammen bedeutet, dass die Konstante operatorIndex in der Funktion compute(expression : String) ebenfalls ein Optional ist, da wir der Konstanten keinen expliziten Typ zugewiesen haben. Lass das Programm mal laufen. Es müsste funktionieren. 

Was jedoch würde passieren, wenn du versehentlich den arithmetischen Ausdruck “20 = 7” als Argument der Funktion compute(expression : String) übergibst? Denk mal kurz darüber nach – oder programmiere diesen Fall einfach. Hier der Sourcecode:

let myWrongAddition = "20 = 7"
let wrongAddition = compute(expression: myWrongAddition)


Du wirst folgende Fehlermeldung bekommen: fatal error: unexpectedly found nil while unwrapping an Optional value. Und zwar in der Zeile mit der Anweisung: let operatorSymbol = expression[operatorIndex!]. 

Was ist genau passiert? Nun, der von der Funktion getOperatorIndex(_ expression : String) zurückgelieferte Wert ist natürlich nil, da der Operator “=” von der Funktion nicht überprüft wird. Infolgedessen führt das Unwrapping operatorIndex! in der Anweisung let operatorSymbol = expression[operatorIndex!] zu einem Fehler, da nur aus Optionals Werte ausgepackt werden können. Im Grunde genommen waren wir hier nicht achtsam, denn wir dürfen nie ein Optional auspacken, wenn wir nicht sicher sind, dass der Wert nicht nil ist. Um diese Art Fehler zu verhindern, können wir Folgendes tun:

func compute(expression : String) -> Double? {
    if let operatorIndex  = getOperatorIndex(expression) {
        let operatorSymbol = expression[operatorIndex]
        let leftUntrimmed  = expression[..<operatorIndex]
        let leftStr        = leftUntrimmed.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
        let left           = Double(leftStr)!
        let rightUntrimmed = expression[expression.index(after: operatorIndex)..<expression.endIndex]
        let rightStr       = rightUntrimmed.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
        let right          = Double(rightStr)!

        if (operatorSymbol == "+") {
            return add(value1: left , value2: right)
        }

        if (operatorSymbol == "-") {
            return subtract(value1: left , value2: right)
        }

        if (operatorSymbol == "*") {
            return multiply(value1: left , value2: right)
        }

        return divide(value1: left , value2: right)
    }

    return nil
}

let myAddition = "20 + 7"
let mySubtraction = "35 - 8"
let myMultiplication = "13.5 * 2"
let myDivision = "54 / 2"
let myWrongAddition = "20 = 7"

let additionResult = compute(expression: myAddition)
let subtractionResult = compute(expression: mySubtraction)
let multiplicationResult = compute(expression: myMultiplication)
let divisionResult = compute(expression: myDivision)
let wrongAdditionResult = compute(expression: myWrongAddition)

print("Die Summe \(myAddition) ist gleich \(additionResult)")
print("Die Subtraktion \(mySubtraction) ist gleich \(subtractionResult)")
print("Die Multiplikation \(myMultiplication) ist gleich \(multiplicationResult)")
print("Die Subtraktion \(myDivision) ist gleich \(divisionResult)")
print("Die fehlerhafte Summe \(myWrongAddition) ist gleich \(wrongAdditionResult)")


Zunächst einmal habe ich den Aufruf der Funktion getOperatorIndex(_ expression : String) in ein Optional Binding eingebettet. Somit wird der gesamte Sourcecode nur dann ausgeführt, wenn der Rückgabewert der Funktion getOperatorIndex(_ expression : String) nicht nil ist. Wenn der Rückgabewert doch nil ist, wird am Ende der Funktion compute(expression : String) nil zurückgeliefert. Das wiederum erfordert, dass der Rückgabetyp jetzt Double? ist. Damit wären alle Veränderungen der Funktion beschrieben. Leider wird jetzt jede print()Anweisung mit der Warnung String interpolation produces a debug description for an optional value; did you mean to make this explicit? markiert. Warnungen sind keine Fehler, sie geben aber wichtige Hinweise auf problematischen Sourcecode, der erst zur Laufzeit zu Fehlern führen kann. Warnungen sollte man daher nicht einfach ignorieren. Um jedoch die Warnungen besser verstehen zu können, kannst du das Programm ruhig neu kompilieren und ausführen. Du wirst folgende Ausgaben bekommen: 

Die Summe 20 + 7 ist gleich Optional(27.0)
Die Subtraktion 35 - 8 ist gleich Optional(27.0)
Die Multiplikation 13.5 * 2 ist gleich Optional(27.0)
Die Division 54 / 2 ist gleich Optional(27.0)
Die fehlerhafte Summe 20 = 7 ist gleich nil

Wir können sehen, dass die Konstanten myAddition, mySubtraction, myMultiplication und myDivision jeweils ein Optional enthalten und nicht den eigentlichen Wert. Das ist ja auch kein Wunder: Wir haben ja die Rückgabewerte nicht ausgepackt. Die letzte Konstante myWrongAddition enthält wie erwartet nil. Und jetzt kann man vielleicht die Warnung besser verstehen. Sie will uns lediglich darauf hinweisen, dass wir vielleicht das Auspacken vergessen haben.
Und, hast du eine Idee, wie man die Warnungen loswerden kann? Probiere es aus. Du hast alle Informationen. Im nächsten Blogpost werde ich dir meine Lösung zeigen. Bis dahin, tschüss und bleib sauber.

    
Über Juan Carlos Flores

Juan Carlos Flores ist seit 2006 Software Architekt bei itemis. Als diplomierter Informatiker wirkt er seit 20 Jahre in vielen Enterprise-Projekten mit und trägt zu Problemlösungen bei.