Tracks
/
Swift
Swift
/
Übungen
/
Double-Null0111: Closures sind für immer
Double-Null0111: Closures sind für immer

Double-Null0111: Closures sind für immer

Lernübung

Einführung

Closures sind in Swift in sich geschlossene Blöcke aus Code, denen du Parameter übergeben kannst, um ihre Berechnung auszulösen und Werte zurückzugeben. Closures erfassen außerdem Werte aus ihrer Umgebung und verwenden sie in ihren Berechnungen. Da sie in sich geschlossen sind, kannst du sie in einem Programm wie andere Werte weiterreichen oder Konstanten und Variablen zuweisen.

Closures klingen vielleicht sehr nach Swift-Funktionen; das tun sie auch, und das aus gutem Grund. Funktionen in Swift sind Spezialfälle von Closures, die einen Namen haben müssen und mit einer etwas anderen Syntax definiert werden.

Funktionen in Swift sind zwar technisch gesehen Closures, aber wenn in Swift von „Closures“ die Rede ist, sind damit die Closure-Ausdrücke gemeint. Closure-Ausdrücke bestehen aus einer Parameterliste, gefolgt von einem Rückgabetyp und dem Schlüsselwort in, gefolgt vom Rumpf des Closure, und das Ganze steht in einem Paar geschweifter Klammern:

{ (a: Double, b: Double) -> Double in
  return (a + b) / 2.0
}

Das definiert einen Closure-Ausdruck vom Typ (Double, Double) -> Double, und du kannst ihn ähnlich ausführen wie eine Funktion dieses Typs, indem du ihn auf Parameter des passenden Typs anwendest:

{ (a: Double, b: Double) -> Double in
  return (a + b) / 2.0
}(10.0, 15.0)
// returns 12.5

Es ist nicht sehr praktisch, jedes Mal den vollständigen Closure-Ausdruck auszuschreiben, wenn du ihn ausführen willst. Deshalb kannst du Closures wie jeden anderen Wert in Swift einem Namen zuweisen. Danach rufst du sie auf, indem du den Namen auf die Parameter anwendest:

let mean = { (a: Double, b: Double) -> Double in
  (a + b) / 2.0
}

mean(13.0, 100.0)
// returns 56.5

Wie oben zu sehen ist, können Closures wie reguläre Funktionen das Schlüsselwort return weglassen, wenn der Rumpf des Closure ein einzelner Ausdruck ist.

Meistens werden Closures mit Funktionen höherer Ordnung verwendet, entweder als Parameter übergeben oder als Ergebnis zurückgegeben:

[11, 75, 3, 99, 53].contains(where: { (x: Int) -> Bool in x > 100 })
// returns false

["apple", "ball", "carrot"].sorted(by: { (s1: String, s2: String) -> Bool in s1.count < s2.count })
// returns ["ball", "apple", "carrot"]

func makeAdder(base: Int) -> (Int) -> Int {
  { (x: Int) -> Int in base + x }
}

Anleitung

Hallo, Agent Double-Null0111, da bist du ja wieder.

Die Kräfte von UMBRA machen wieder Ärger, und es liegt an dir, ihren neuesten Plan zu vereiteln. Unser Geheimdienst teilt uns mit, dass diese Minions planen, auf dem Gouverneursball eine Stinkbombe zu zünden.

Da die Minions so tollpatschig sind, schärfen sie die Stinkbomben in ihren Laboren und Büros oft versehentlich. Und weil sie immer wieder vergessen, wie man ihre Stinkbomben entschärft, haben sie ein System implementiert, das die Entschärfungsanweisungen generiert, in denen die Reihenfolge aufgelistet ist, in der die Drähte anhand der ID-Nummer der Bombe durchtrennt werden müssen.

Deine Aufgabe ist es, den Code zu schreiben, mit dem du die Stinkbombe auf dem Ball entschärfen kannst, bevor sie explodiert.

1. Schreibe eine Closure, die zwei Drähte vertauscht

Jede Stinkbombe hat drei verschiedenfarbige Drähte. Schreibe eine Closure, die ein (String, String, String)-Tupel entgegennimmt und das Tupel zurückgibt, bei dem die Reihenfolge der ersten beiden Elemente vertauscht ist. Weise diese Closure dem Namen flip zu.

Wichtig ist, dass der Typ ChangeClosure (oder ein Sendable-Typ) sein muss. Die Typdefinition ist in der Stub-Datei angegeben. Andernfalls kompiliert der Code nicht.

flip(("red", "yellow", "blue"))
// Returns ("yellow", "red", "blue")

2. Schreibe eine Closure, die die Drähte rotiert

Schreibe eine Closure, die ein (String, String, String)-Tupel entgegennimmt und das Tupel zurückgibt, bei dem die Reihenfolge der Drähte nach links rotiert wurde. Weise diese Closure dem Namen rotate zu.

Wichtig ist, dass der Typ ChangeClosure (oder ein Sendable-Typ) sein muss. Die Typdefinition ist in der Stub-Datei angegeben.

rotate(("red", "yellow", "blue"))
// Returns ("yellow", "blue", "red")

3. Implementiere einen Generator zum Mischen der Drähte

Um die Reihenfolge herauszufinden, in der die Drähte der Stinkbombe durchtrennt werden müssen, musst du eine Funktion implementieren, die anhand der ID-Nummer der Stinkbombe die passende Mischfunktion generiert.

Implementiere die Funktion:

makeShuffle(
  flipper: @escaping ((String, String, String)) -> (String, String, String),
  rotator: @escaping ((String, String, String)) -> (String, String, String)
) -> ([UInt8], (String, String, String)) -> (String, String, String)

die als Eingabe eine Closure, die zwei Drähte vertauscht, und eine Closure, die die drei Drähte rotiert, entgegennimmt und eine Closure zurückgibt. Diese zurückgegebene Closure nimmt die ID-Nummer der Stinkbombe und die Reihenfolge der drei Drähte entgegen und berechnet dann die Reihenfolge, in der die Drähte durchtrennt werden müssen. Die ID-Nummer besteht aus acht Bits, und jedes Bit bestimmt, ob die Drähte vertauscht oder rotiert werden.

Für jedes Bit in der ID-Nummer, beginnend mit dem rechtesten Bit, wendest du die flipper-Closure auf das Tupel der Drähte an, wenn das Bit 0 ist, und du wendest die rotator-Closure an, wenn es 1 ist, was den neuen Zustand der Drähte ergibt. Nachdem die entsprechenden Closures für alle acht Bits der ID angewendet wurden, ist der endgültige Zustand der Drähte die Reihenfolge, in der sie durchtrennt werden müssen.

let shuffler = makeShuffle(flipper: flip, rotator: rotate)
// Returns ([UInt8], (String, String, String)) -> (String, String, String)
shuffler([1, 0, 0, 1, 1, 0, 1, 1], ("red", "yellow", "blue"))
// Returns ("red", "blue", "yellow")
Über GitHub bearbeiten Der Link öffnet sich in einem neuen Fenster oder Tab
Swift Exercism

Bereit, mit Double-Null0111: Closures sind für immer zu starten?

Melde dich bei Exercism an, um Swift mit 35 Konzepte116 Übungen und echtem menschlichen Mentoring zu lernen und zu meistern, alles kostenlos.