Tag 2 - Komplexere Datentypen

02. Jan. 2024

Tag 2

Arrays

Mit Arrays kann man mehrere Werte zusammen speichern. Ihre Speicherplätze werden mit dem Index 0 aufwärts gezählt und durch eckige Klammern gekennzeichnet. Sie können durch Werte direkt deklariert werden:

let john = "John Lennon"
let paul = "Paul McCartney"
let george = "George Harrison"
let ringo = "Ringo Starr"

let beatles = [john, paul, george, ringo]

beatles[2]

Und die Typenbezeichnung kann Hinweise darauf geben, welche Datentypen gespeichert werden können: [String], [Int], [Double] und [Bool]. Hierbei ist zu beachten, dass in Swift nur derselbe Datentyp gespeichert werden kann.

Sets

Währned in einem Array die Reihenfolge der Werte beibehalten wird und Werte auch mehrmals gespeichert werden können, ist dies in einem Set nicht der Fall. Die Deklaration ist etwas umfangreicher als mit einem Array:

let colors = Set(["red", "green", "blue"])

Da die Werte immer eindeutig (bzw. unique) sind, wäre das folgende Set dem oben ähnlich (nicht gleich oder identisch, da die Reihenfolge verschieden sein kann):

let colors2 = Set(["red", "green", "blue", "red", "blue"])

Durch die quasi willkürliche Reihenfolge kann Swift Daten so effizient wie möglich speichern.

Tuples

Tupel ermöglichen es, mehrere Werte zusammen in als ein einziger Wert zu speichern. Das klingt vielleicht wie Arrays, aber zu einem Tupel können keine Elemente hinzufügt oder entfernt werden (ihre Größe ist also fix), der Typ der Elemente in einem Tupel können nicht geändert werden (sie haben immer den gleichen Datentyp, mit dem sie deklariert wurden), und auf Elemente in einem Tupel kann unter Nutzung einer numerischen Positionen oder durch eine Bezeichnung zugreifen werden (aber Swift lässt keine Zahlen oder Namen auslesen, die nicht existieren). Deklariert werden Tuples wie folgt:

var name = (first: "Max", last: "Zangs", age: 32)

Der Datenzugriff funktioniert dann wie folgt:

name.0

oder:

name.first

Tuples eignen sich besonders gut, um zusammengehörige Daten zusammenzufassen. Denn das oben deklarierte Tuple enthält sowohl zwei String als auch ein Int, auf die durch ihre Bezeichnungen first, last und age eindeutig und unmissverständlich zugegriffen werden kann.

Arrays vs Sets vs Tuples

Arrays, Sets und Tupels sehen auf den ersten Blick recht ähnlich aus, dienen aber unterschiedlichen Verwendungen. Zur deren Nutzung gibt es einige Regeln. Wenn eine spezifische, feste Sammlung zusammengehöriger Werte gespeichert werden soll, bei der jedes Element eine genaue Position oder einen Namen hat, sollte ein Tupel benutz werden:

let address = (number: 193, street: "Rosenheimer Str.", city: "München")

Wenn eine Sammlung von Werten benötigt wird, die eindeutig sein müssen, oder wenn sehr schnell überprüft werden muss, ob ein spezifisches Element in der Sammlung enthalten ist, sollten eine Set eingesetzt werden:

let set = Set(["aardvark", "astronaut", "azalea"])

Wenn eine Sammlung von Werten benötigt wird, die Duplikate enthalten können soll, oder/und wenn die Reihenfolge der Elemente wichtig ist, sollten ein Array benutzt werden:

let pythons = ["Eric", "Graham", "John", "Michael", "Terry", "Terry"]

Hierbei sind Arrays sind bei weitem der häufigste der drei Typen.

Dictionaries

Dictionaries sind Sammlungen von Werten, genau wie Arrays, aber anstatt Werte mit einer ganzzahligen Positionsangaben zu speichern, kann man auf die Werte zugreifen, indem irgendein anderer Wert benutzt wird.

Die gebräuchlichste Art, Daten in Dictionaries zu speichern, ist die Benutzung von Zeichenketten oder Strings. Man kann z.B. ein Dictionary herstellen, das die Höhe von Schülern unter Benutzung ihres Namens speichert:

let heights = [
    "Max": 1.75,
    "Ed": 1.78
]

Genau wie bei Arrays beginnen und enden Dictionaries mit eckigen Klammern, und jedes Element wird durch ein Komma getrennt. Man benutzt jedoch auch einen Doppelpunkt, um den zu speichernden Wert (z.B. 1.75), von dem Bezeichner (z.B. "Max") zu trennen, unter dem der Wert gespeichert werden soll. Diese Bezeichner werden auch als Schlüssel (engl. keys) bezeichnet, und man benutzt sie, um Daten aus dem Dictionary auszulesen:

heights["Max"]

Es ist zu beachten, dass unter Verwendung von Typenbezeichnungen, Dictionaries in eckigen Klammern mit einem Doppelpunkt zwischen dem Bezeichner und dem Werttyp geschrieben. Zum Beispiel: [String: Double] und [String: String].

Standardwert

Wenn man versucht, einen Wert aus einem Dictionary unter einem Key auszulesen, der nicht existiert, gibt Swift null zurück - also gar nichts. Obwohl dies vielleicht gewünscht sein mag, gibt es eine Alternative. Man kann einen Standardwert angeben, der benutzt wird, wenn es für einen Key keinen Eintrag im Dictionary gibt.

let grades = [
    "Mathematik": "gut bestanden",
    "Englisch": "bestanden",
    "Sport": "sehr gut bestanden",
    "Physik": "knapp bestanden"
]

grades["Französisch", default: "Fach nicht belegt"]

Leere Sammlungen erstellen

Oben wurde immer gezeigt, wie Sammlungen mit Werten erstellt werden können. Möchte man aber nur eine Sammlung als Variable deklarieren, um sie später mit Werten zu füllen, ist das natürlich auch möglich.

Ein leeres Dictionary erstell man wie folgt:

var teams = [String: String]()

Und später können Werte hinzufügen, etwa so:

teams["Paul"] = "Rot"

Auf ähnliche Weise kann ein leeres Array deklariert werden, z.B. um ganze Zahlen zu speichern:

var results = [Int]()

Die Ausnahme ist das Deklarieren eines leeren Sets:

var words = Set<String>()
var numbers = Set<Int>()

Das liegt daran, dass Swift eine spezielle Syntax nur für Wörterbücher und Arrays hat; andere Datentypen müssen die spitzen Klammern als Syntax wie Sätze benutzen. Das ist eine universelle Schreibweise, mit der auch Arrays und Dictionaries ertellt werden können:

var scores = Dictionary<String, Int>()
var results = Array<Int>()

Enumeration

Aufzählungen oder Enumerations, die kurz Enums genannt werden, sind eine Möglichkeit, Gruppen von zusammengehörigen Werten so zu definieren, dass sie leichter zu benutzen sind. Wenn z.B. ein Erfolg oder Misserfolg einer Arbeit durch eine Variable angegeben werden soll, kann man dies mit einem String tun:

let resultVague = "failure"

Vertippt man sich oder schreibt das Ergebnis mit einem Großbuchstaben, könnte dies zu Verwechslungen führen, auch wenn die Bedeutung des String dieselbe ist. Mit enums können Ergebnisse definieren, die entweder einen Erfolg oder Misserfolg eindeutig angeben:

enum Results {
    case success
    case failure
}

let resultPrecise = Result.failure

Somit kann das Ergebnis immer genau und ohne Verwechslung gespeichert werden.

Enums mit zugeordneten Werten

Enums können nicht nur einen einfachen Wert speichern, sondern auch jedem Fall Werte zuordnen. Auf diese Weise können Enums zusätzliche Informationen so zugeordnet werden, dass sie differenziertere Daten repräsentieren.

enum Activities {
    case bored
    case running(target: String)
    case talking(topic: String)
    case singing(volume: Int)
}

Somit kann man präziser angeben, welche Aktivität grade wie ausgeübt wird:

let talking = Activity.talking(topic: "politics")

Es können auch mehrere Werte einem Fall zugeordnet werden:

enum Weather {
    case sunny
    case windy(speed: Int)
    case rainy(chance: Int, amount: Int)
}

Rohwerte von Enums

Manchmal muss man Enums einen Wert zuweisen können, damit sie eine Bedeutung haben. Auf diese Weise können Enums dynamisch herstellt und auch auf verschiedene Weise benutzt werden. Möchte man z.B. die Planeten unseres Sonnensystems speichern:

enum Planet: Int {
    case mercury
    case venus
    case erde
    case mars
}

weist Swift jedem dieser Fälle der Reihenfolge nach eine Nummer zu, und beginnt bei 0 . Der Erde wird in diesem Beispiel die Nummer 2 zugewiesen, obwohl sie der dritte Planet unseres Sonnensystems ist. Dem kann abgeholfen werden, indem

enum Planet: Int {
    case mercury = 1
    case venus
    case earth
    case mars
}

Jetzt weist Swift dem Merkur eine 1 zu und zählt von da an aufwärts, was bedeutet, dass die Erde nun der dritte Planet ist. Die Erde kann nun also auch wie folgt als Enum gespeichert werden:

let earth = Planet(rawValue: 3)

Diese Rohwerte erlauben es, Enums miteinander zu vergleichen, z.B. bei Skalen oder Werten mit einer Reihenfolge.