Funktionen & Closures
Funktionen mit Argument-Labels, Default-Werten, Variadic-Parameter und Closures - die Grundlage von vielem in Swift, inklusive SwiftUI.
Inhaltsverzeichnis
Funktionen & Closures
Swift hat viele nette Details bei Funktionen, die du in anderen Sprachen so nicht findest. Und Closures sind der Schluessel zu vielen modernen Swift-APIs - besonders SwiftUI.
Grundform
func gruessen(name: String) {
print("Hallo, \(name)!")
}
gruessen(name: "Anna")
Auffaellig: Beim Aufruf gibst du den Argument-Namen an. Das macht Swift-Code sehr lesbar.
Rueckgabewert
func addieren(a: Int, b: Int) -> Int {
return a + b
}
let summe = addieren(a: 3, b: 4) // 7
Bei einzeiligen Funktionen kannst du return weglassen:
func addieren(a: Int, b: Int) -> Int {
a + b
}
Argument-Labels
Swift unterscheidet zwischen Label und Parameter-Name:
func gruessen(person name: String) {
print("Hallo, \(name)!")
}
gruessen(person: "Anna")
personist das Label (beim Aufruf sichtbar)nameist der Parameter-Name (innen genutzt)
Label unterdruecken
Mit _:
func addieren(_ a: Int, _ b: Int) -> Int {
a + b
}
addieren(3, 4) // kein Label noetig
Warum Labels?
Labels machen den Aufruf selbstdokumentierend:
move(from: .home, to: .work)
calculate(cost: 100, withDiscount: 0.1)
send(message: "Hallo", to: "Anna")
Default-Werte
func gruessen(name: String = "Welt", gruss: String = "Hallo") {
print("\(gruss), \(name)!")
}
gruessen() // "Hallo, Welt!"
gruessen(name: "Anna") // "Hallo, Anna!"
gruessen(gruss: "Guten Morgen", name: "Anna")
Variadic Parameter
Beliebig viele Argumente mit ...:
func summe(_ zahlen: Int...) -> Int {
zahlen.reduce(0, +)
}
summe(1, 2, 3) // 6
summe(10, 20, 30, 40) // 100
Mehrere Rueckgabewerte mit Tupeln
func minMax(zahlen: [Int]) -> (min: Int, max: Int)? {
guard let erste = zahlen.first else { return nil }
var min = erste
var max = erste
for z in zahlen {
if z < min { min = z }
if z > max { max = z }
}
return (min, max)
}
if let (min, max) = minMax(zahlen: [3, 1, 4, 1, 5, 9, 2]) {
print("min: \(min), max: \(max)")
}
Benannte Tuple-Elemente machen den Aufruf noch klarer.
inout Parameter
Wenn die Funktion einen uebergebenen Wert aendern soll:
func verdoppeln(_ zahl: inout Int) {
zahl *= 2
}
var x = 5
verdoppeln(&x) // & beim Aufruf
print(x) // 10
Selten genutzt - meistens bessere Rueckgabewert-Loesung.
Funktionen als Werte
Funktionen sind First-Class Citizens:
func verdoppeln(_ n: Int) -> Int { n * 2 }
let f: (Int) -> Int = verdoppeln
print(f(5)) // 10
An andere Funktionen uebergeben:
func wende(_ f: (Int) -> Int, auf zahl: Int) -> Int {
f(zahl)
}
print(wende(verdoppeln, auf: 5)) // 10
Closures
Eine Closure ist eine anonyme Funktion:
let verdoppeln = { (n: Int) -> Int in
n * 2
}
print(verdoppeln(5)) // 10
Der Aufbau: { (parameter) -> Rueckgabe in anweisungen }.
Kuerzere Syntax
Swift leitet viel her:
let zahlen = [1, 2, 3, 4, 5]
// Vollstaendig
let verdoppelt = zahlen.map({ (n: Int) -> Int in return n * 2 })
// Typ-Inferenz
let v2 = zahlen.map({ n in return n * 2 })
// Ohne return bei Einzeilern
let v3 = zahlen.map({ n in n * 2 })
// Shorthand-Argumente
let v4 = zahlen.map({ $0 * 2 })
// Trailing Closure - am gelaeufigsten
let v5 = zahlen.map { $0 * 2 }
Alle fuenf sind gleichwertig - im Alltag nutzt du meist Form 4 oder 5.
Trailing Closure
Wenn die letzte Parameter einer Funktion eine Closure ist:
zahlen.forEach { zahl in
print(zahl)
}
zahlen.filter { $0 > 2 }.map { $0 * 10 }
Das ist das Muster, das SwiftUI-Code so lesbar macht:
Button("Speichern") {
speichereDaten()
}
Die { speichereDaten() } ist eine trailing Closure - kein magischer Syntax.
$0, $1, $2
Statt in kannst du direkt auf Argumente zugreifen:
zahlen.sorted { $0 < $1 }
zahlen.reduce(0) { $0 + $1 }
Higher-Order Collection Methods
let zahlen = [1, 2, 3, 4, 5]
zahlen.map { $0 * 2 } // [2, 4, 6, 8, 10]
zahlen.filter { $0 % 2 == 0 } // [2, 4]
zahlen.reduce(0, +) // 15
zahlen.sorted() // [1, 2, 3, 4, 5]
zahlen.sorted { $0 > $1 } // [5, 4, 3, 2, 1]
zahlen.contains { $0 > 3 } // true
zahlen.first { $0 > 3 } // Optional(4)
Ketten bilden natuerliche Pipelines:
let ergebnis = zahlen
.filter { $0 > 1 }
.map { $0 * 10 }
.reduce(0, +)
print(ergebnis) // 140
Capturing
Closures โfangenโ Variablen aus ihrem Scope ein:
func makeZaehler() -> () -> Int {
var count = 0
return {
count += 1
return count
}
}
let weiter = makeZaehler()
print(weiter()) // 1
print(weiter()) // 2
print(weiter()) // 3
Jeder Aufruf von makeZaehler() erzeugt einen eigenen count.
Zusammenfassung
- Argument-Labels und Parameter-Namen sind getrennt (
person name: String) - Default-Werte und Variadic Parameters (
...) sind eingebaut - Closures sind anonyme Funktionen mit sehr kompakter Syntax
- Trailing Closure ist der Schluessel zu idiomatischem Swift
- Higher-Order Methods (
map,filter,reduce) machen Datenverarbeitung elegant
Im naechsten Kapitel: Structs, Classes und Enums - die drei Wege, eigene Typen zu definieren.