Skip to content

[Swift 6]: Update bomb-defuser and clousers #838

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 21 additions & 15 deletions concepts/closures/about.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,40 @@
# About

[Closures][closures] in Swift are self-contained blocks of code that can be passed parameters to trigger their computation and return values. Closures also capture values from their environment and use them in their computations. As they are self contained, they may be passed around in a program like other values or assigned to constants and variables.
[Closures][closures] in Swift are self-contained blocks of code that can be passed parameters to trigger their computation and return values
Closures also capture values from their environment and use them in their computations.
As they are self contained, they may be passed around in a program like other values or assigned to constants and variables.

Closures may sound a lot like Swift functions; they do, and for good reason. Functions in swift are just special cases of closures which are required to have a name and are defined using a slightly different syntax.
Closures may sound a lot like Swift functions; they do, and for good reason.
Functions in Swift are just special cases of closures which are required to have a name and are defined using a slightly different syntax.

While functions in Swift are technically closures, when people refer to "closures" in Swift, they are referring to [closure expressions][closure-expressions]. Closure expressions are written as a parameter list followed by a return type and the keyword `in` followed by the body of the closure, all contained in a pair of curly braces:
While functions in Swift are technically closures, when people refer to "closures" in Swift, they are referring to [closure expressions][closure-expressions].
Closure expressions are written as a parameter list followed by a return type and the keyword `in` followed by the body of the closure, all contained in a pair of curly braces:

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

This defines a closure expression of type `(Double, Double) -> Double`, and it can be executed in a similar manner to a function of this type, by applying it to parameters of the appropriate type:

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

As it's not very convenient to write out the full closure expression every time one wants to execute it, closures can be assigned to names, like any other value in Swift. They can then be called by applying the name to the parameters:
As it's not very convenient to write out the full closure expression every time one wants to execute it, closures can be assigned to names, like any other value in Swift.
They can then be called by applying the name to the parameters:

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

mean(13.0, 100.0)
// => 56.5
// returns 56.5
```

As seen above, closures, like regular functions, may omit the `return` keyword if the body of the closure is a single expression.
Expand All @@ -38,10 +43,10 @@ Most often, closures are used with higher-order functions, either passed in as p

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

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

func makeAdder(base: Int) -> (Int) -> Int {
{ (x: Int) -> Int in base + x }
Expand All @@ -50,7 +55,8 @@ func makeAdder(base: Int) -> (Int) -> Int {

## Inferring closure types

When the return type or parameter types can be [inferred from context][inferring-closure-types], they can be omitted from the closure expression. Additionally, if the parameter types can be omitted, so can the parentheses around the parameter names:
When the return type or parameter types can be [inferred from context][inferring-closure-types], they can be omitted from the closure expression.
Additionally, if the parameter types can be omitted, so can the parentheses around the parameter names:

```swift
let mean: (Double, Double) -> Double = { a, b in a + b / 2.0 }
Expand Down Expand Up @@ -81,6 +87,6 @@ add5(x: 10)
- Parameters in closures can not have a default parameter value.
- Parameters in closures may be variadic parameters, but they must have a name and type annotation.

[closures]: https://docs.swift.org/swift-book/LanguageGuide/Closures.html#
[closure-expressions]: https://docs.swift.org/swift-book/LanguageGuide/Closures.html#ID95
[inferring-closure-types]: https://docs.swift.org/swift-book/LanguageGuide/Closures.html#ID98
[closures]: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/closures/
[closure-expressions]: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/closures/#Closure-Expressions
[inferring-closure-types]: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/closures/#Inferring-Type-From-Context
52 changes: 49 additions & 3 deletions concepts/closures/introduction.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,57 @@
# About

Closures in Swift are self-contained blocks of code that can be passed parameters to trigger their computation and return values. Closures also capture values from their environment and use them in their computations. As they are self contained, they may be passed around in a program like other values or assigned to constants and variables.
[Closures][closures] in Swift are self-contained blocks of code that can be passed parameters to trigger their computation and return values
Closures also capture values from their environment and use them in their computations.
As they are self contained, they may be passed around in a program like other values or assigned to constants and variables.

While functions in Swift are technically closures, when people refer to "closures" in Swift, they are referring to closure expressions. Closure expressions are written as a parameter list followed by a return type and the keyword `in` followed by the body of the closure, all contained in a pair of curly braces:
Closures may sound a lot like Swift functions; they do, and for good reason.
Functions in Swift are just special cases of closures which are required to have a name and are defined using a slightly different syntax.

While functions in Swift are technically closures, when people refer to "closures" in Swift, they are referring to [closure expressions][closure-expressions].
Closure expressions are written as a parameter list followed by a return type and the keyword `in` followed by the body of the closure, all contained in a pair of curly braces:

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

This defines a closure expression of type `(Double, Double) -> Double`, and it can be executed in a similar manner to a function of this type, by applying it to parameters of the appropriate type:

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

As it's not very convenient to write out the full closure expression every time one wants to execute it, closures can be assigned to names, like any other value in Swift.
They can then be called by applying the name to the parameters:

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

mean(13.0, 100.0)
// returns 56.5
```

As seen above, closures, like regular functions, may omit the `return` keyword if the body of the closure is a single expression.

Most often, closures are used with higher-order functions, either passed in as parameters or returned as results:

```swift
[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 }
}
```

[closures]: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/closures/
[closure-expressions]: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/closures/#Closure-Expressions
4 changes: 2 additions & 2 deletions concepts/closures/links.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[
{
"url": "http://goshdarnclosuresyntax.com",
"description": "Gosh Darn Closure Syntax"
"url": "https://docs.swift.org/swift-book/documentation/the-swift-programming-language/closures/",
"description": "Swift Book: Closures"
}
]
30 changes: 21 additions & 9 deletions exercises/concept/bomb-defuser/.docs/instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,38 @@

Hello again, Agent Double-Null0111.

The forces of UMBRA are acting up again, and it is up to you to foil their latest scheme. Our intel branch informs us that those Minions are planning to set off a stink-bomb at the Governor's ball.
The forces of UMBRA are acting up again, and it is up to you to foil their latest scheme.
Our intel branch informs us that those Minions are planning to set off a stink-bomb at the Governor's ball.

Since the minions are so clumsy, they often accidentally arm the stink-bombs in their labs and offices. And because they keep forgetting how to disarm their stink-bombs, they have implemented a system generate the disarming instructions that list the order the wires must be cut from the bomb's ID number.
Since the minions are so clumsy, they often accidentally arm the stink-bombs in their labs and offices.
And because they keep forgetting how to disarm their stink-bombs, they have implemented a system generate the disarming instructions that list the order the wires must be cut from the bomb's ID number.

Your job is to write the code that will allow you to disarm the stink-bomb at the ball before it goes off.

## 1. Write a closure to flip two wires

There are three differently colored wires on each stink-bomb. Write a closure that takes a `(String, String, String)` tuple and returns the tuple with the order of the first two elements flipped. Assign this closure to the name `flip`.
There are three differently colored wires on each stink-bomb.
Write a closure that takes a `(String, String, String)` tuple and returns the tuple with the order of the first two elements flipped.
Assign this closure to the name `flip`.

It is important to note that the type has to be the `ChangeClosure` (or a sendable type), the type definition is given in the stub file.
Otherwise, the code will not compile.

```swift
flip(("red", "yellow", "blue"))
// => ("yellow", "red", "blue")
// returns ("yellow", "red", "blue")
```

## 2. Write a closure to rotate the wires

Write a closure that takes a `(String, String, String)` tuple and returns the tuple with the order of the wires rotated to the left. Assign this closure to the name `rotate`.
Write a closure that takes a `(String, String, String)` tuple and returns the tuple with the order of the wires rotated to the left.
Assign this closure to the name `rotate`.

It is important to note that the type has to be the `ChangeClosure` (or a sendable type), the type definition is given in the stub file.

```swift
rotate(("red", "yellow", "blue"))
// => ("yellow", "blue", "red")
// returns ("yellow", "blue", "red")
```

## 3. Implement a wire shuffle generator
Expand All @@ -39,14 +49,16 @@ makeShuffle(
) -> (UInt8, (String, String, String)) -> (String, String, String)
```

which takes as input a closure that flips two wires and a closure that rotates the three wires and returns a closure. This returned closure takes the ID number of the stink-bomb and the order of the three wires, and then computes the order the wires need to be cut. This is computed as follows:
which takes as input a closure that flips two wires and a closure that rotates the three wires and returns a closure.
This returned closure takes the ID number of the stink-bomb and the order of the three wires, and then computes the order the wires need to be cut.
This is computed as follows:

For each bit in the ID number, starting with the rightmost bit, you will apply the `flipper` closure to the wires tuple if the bit is a 0 and you will apply the `rotator` closure if it is a 1 giving the new state of the wires.
After the appropriate closures have been applied for all eight bits of the ID, the final state of the wires is the order they need to be cut in.

```swift
let shuffler = makeShuffle(flipper: flip, rotator: rotate)
// => (UInt8, (String, String, String)) -> (String, String, String)
// returns (UInt8, (String, String, String)) -> (String, String, String)
shuffler(155, ("red", "yellow", "blue"))
// => ("red", "blue", "yellow")
// returns ("red", "blue", "yellow")
```
30 changes: 18 additions & 12 deletions exercises/concept/bomb-defuser/.docs/introduction.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,40 @@
# Introduction
# About

Closures in Swift are self-contained blocks of code that can be passed parameters to trigger their computation and return values. Closures also capture values from their environment and use them in their computations. As they are self contained, they may be passed around in a program like other values or assigned to constants and variables.
[Closures][closures] in Swift are self-contained blocks of code that can be passed parameters to trigger their computation and return values
Closures also capture values from their environment and use them in their computations.
As they are self contained, they may be passed around in a program like other values or assigned to constants and variables.

Closures may sound a lot like Swift functions; they do, and for good reason. Functions in swift are just special cases of closures which are required to have a name and are defined using a slightly different syntax.
Closures may sound a lot like Swift functions; they do, and for good reason.
Functions in Swift are just special cases of closures which are required to have a name and are defined using a slightly different syntax.

While functions in Swift are technically closures, when people refer to "closures" in Swift, they are referring to [closure expressions][closure-expressions]. Closure expressions are written as a parameter list followed by a return type and the keyword `in` followed by the body of the closure, all contained in a pair of curly braces:
While functions in Swift are technically closures, when people refer to "closures" in Swift, they are referring to [closure expressions][closure-expressions].
Closure expressions are written as a parameter list followed by a return type and the keyword `in` followed by the body of the closure, all contained in a pair of curly braces:

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

This defines a closure expression of type `(Double, Double) -> Double`, and it can be executed in a similar manner to a function of this type, by applying it to parameters of the appropriate type:

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

As it's not very convenient to write out the full closure expression every time one wants to execute it, closures can be assigned to names, like any other value in Swift. They can then be called by applying the name to the parameters:
As it's not very convenient to write out the full closure expression every time one wants to execute it, closures can be assigned to names, like any other value in Swift.
They can then be called by applying the name to the parameters:

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

mean(13.0, 100.0)
// => 56.5
// returns 56.5
```

As seen above, closures, like regular functions, may omit the `return` keyword if the body of the closure is a single expression.
Expand All @@ -38,14 +43,15 @@ Most often, closures are used with higher-order functions, either passed in as p

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

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

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

[closures]: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/closures/
[closure-expressions]: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/closures/#Closure-Expressions
2 changes: 1 addition & 1 deletion exercises/concept/bomb-defuser/Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.3
// swift-tools-version:6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
//let flip: (String, String, String) -> (String, String, String) = TODO: Please define the flip closure
typealias ChangeClosure = @Sendable ((String, String, String)) -> (String, String, String)

//let flip: ChangeClosure = TODO: Please define the flip closure
//
//
//let rotate: (String, String, String) -> (String, String, String) = TODO: Please define the stopAtOne closure
//let rotate: ChangeClosure = TODO: Please define the rotate closure

func makeShuffle(
flipper: @escaping ((String, String, String)) -> (String, String, String),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,58 +1,61 @@
import XCTest
import Testing
import Foundation

@testable import BombDefuser

final class BombDefuserTests: XCTestCase {
let runAll = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false
let RUNALL = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false

@Suite struct BombDefuserTests {
let stringify = { (tuple: (String, String, String)) in "\(tuple)" }

@Test("flip")
func testFlip() {
let expected = ("Dabba", "Yabba", "Doo")
let got = flip(("Yabba", "Dabba", "Doo"))
XCTAssertEqual(
stringify(expected), stringify(got),
#expect(
stringify(expected) == stringify(got),
"flip((\"Yabba\", \"Dabba\", \"Doo\"): Expected \(expected), got \(got)")
}

@Test("rotate", .enabled(if: RUNALL))
func testRotate() throws {
try XCTSkipIf(true && !runAll) // change true to false to run this test
let expected = ("Dooby", "Doo", "Scooby")
let got = rotate(("Scooby", "Dooby", "Doo"))
XCTAssertEqual(
stringify(expected), stringify(got),
#expect(
stringify(expected) == stringify(got),
"rotate((\"Scooby\", \"Dooby\", \"Doo\"): Expected \(expected), got \(got)")
}

@Test("shuffle", .enabled(if: RUNALL))
func testShuffle1() throws {
try XCTSkipIf(true && !runAll) // change true to false to run this test
let wires = ("Red", "Yellow", "Black")
let shuffle = makeShuffle(flipper: flip, rotator: rotate)
let expected = ("Yellow", "Black", "Red")
let got = shuffle(113, wires)
XCTAssertEqual(
stringify(expected), stringify(got),
#expect(
stringify(expected) == stringify(got),
"shuffle(113, (\"Red\", \"Yellow\", \"Black\")): Expected \(expected), got \(got)")
}

@Test("shuffle with other wires", .enabled(if: RUNALL))
func testShuffle2() throws {
try XCTSkipIf(true && !runAll) // change true to false to run this test
let wires = ("Purple", "Cyan", "Marigold")
let shuffle = makeShuffle(flipper: flip, rotator: rotate)
let expected = ("Marigold", "Cyan", "Purple")
let got = shuffle(253, wires)
XCTAssertEqual(
stringify(expected), stringify(got),
#expect(
stringify(expected) == stringify(got),
"shuffle(253, (\"Purple\", \"Cyan\", \"Marigold\")): Expected \(expected), got \(got)")
}

@Test("shuffle with another set of wires", .enabled(if: RUNALL))
func testShuffle3() throws {
try XCTSkipIf(true && !runAll) // change true to false to run this test
let wires = ("Brown", "Orange", "White")
let shuffle = makeShuffle(flipper: flip, rotator: rotate)
let expected = ("Brown", "Orange", "White")
let got = shuffle(0, wires)
XCTAssertEqual(
stringify(expected), stringify(got),
#expect(
stringify(expected) == stringify(got),
"shuffle(0, (\"Brown\", \"Orange\", \"White\")): Expected \(expected), got \(got)")
}
}