Chapter 8: Collection Iterations With Closures
大綱
Closure basics
Closures are so named because they have the ability to “close over” the variables and constants within the closure’s own scope.
This simply means that a closure can access, store and manipulate the value of any variable or constant from the surrounding context.
Variables and constants used within the body of a closure are said to have been captured by the closure.
var multiplyClosure = { (a: Int, b: Int) -> Int in
return a * b
}
let result = multiplyClosure(4, 2)
Shorthand syntax
var multiplyClosure = { (a: Int, b: Int) -> Int in
return a * b
}
// 簡化return
multiplyClosure = { (a: Int, b: Int) -> Int in
a * b
}
// 簡化型別
multiplyClosure = { (a, b) in
a * b
}
// 簡化參數
multiplyClosure = {
$0 * $1
}
// 完整寫法
func operateOnNumbers(_ a: Int, _ b: Int, operation: (Int, Int) -> Int) -> Int {
let result = operation(a, b)
print(result)
return result
}
let addClosure = { (a: Int, b: Int) in
a + b
}
operateOnNumbers(4, 2, operation: addClosure)
// 簡化1
operateOnNumbers(4, 2, operation: { (a: Int, b: Int) -> Int in
return a + b
})
// 簡化2
operateOnNumbers(4, 2, operation: { $0 + $1 })
// 簡化3
operateOnNumbers(4, 2, operation: +)
// 簡化4
operateOnNumbers(4, 2) {
$0 + $1
}
Closures with no return value
Just like functions, closures aren’t required to do these things.
Void is actually just a typealias for (). This means you could have written () -> Void as () -> ().
let voidClosure: () -> Void = {
print("Swift Apprentice is awesome!")
}
voidClosure()
Capturing from the enclosing scope
func countingClosure() -> () -> Int {
var counter = 0
let incrementCounter: () -> Int = {
counter += 1
return counter
}
return incrementCounter
}
let counter1 = countingClosure()
let counter2 = countingClosure()
// The two counters created by the function are mutually exclusive and count independently
counter1() // 1
counter2() // 1
counter1() // 2
counter1() // 3
counter2() // 2
Custom sorting with closures
// SORTING
let names = ["ZZZZZZ", "BB", "A", "CCCC", "EEEEE"]
names.sorted()
let sortedByLength = names.sorted {
$0.count > $1.count
}
sortedByLength // ["A", "BB", "CCCC", "EEEEE", "ZZZZZZ"]
Iterating over collections with closures
In Swift, collections implement some very handy features often associated with functional programming.
let values = [1, 2, 3, 4, 5, 6]
values.forEach {
print("\($0): \($0*$0)")
}
var prices = [1.5, 10, 4.99, 2.30, 8.19]
let largePrices = prices.filter {
return $0 > 5
}
let largePrice = prices.first {
$0 > 5
}
let salePrices = prices.map {
return $0 * 0.9
}
let userInput = ["0", "11", "haha", "42"]
let numbers1 = userInput.map {
Int($0)
}
let numbers2 = userInput.compactMap {
Int($0)
}
let sum = prices.reduce(0) {
return $0 + $1
}
let stock = [1.5: 5, 10: 2, 4.99: 20, 2.30: 5, 8.19: 30]
let stockSum = stock.reduce(0) {
return $0 + $1.key * Double($1.value)
}
let farmAnimals = ["🐎": 5, "🐄": 10, "🐑": 50, "🐶": 1]
let allAnimals = farmAnimals.reduce(into: []) {
(result, this: (key: String, value: Int)) in
for _ in 0 ..< this.value {
result.append(this.key)
}
}
let removeFirst = prices.dropFirst()
let removeFirstTwo = prices.dropFirst(2)
let removeLast = prices.dropLast()
let removeLastTwo = prices.dropLast(2)
let firstTwo = prices.prefix(2)
let lastTwo = prices.suffix(2)
prices.removeAll()
Key points
Closures are functions without names. They can be assigned to variables and passed as parameters to functions.
Closures have shorthand syntax that makes them a lot easier to use than other functions.
A closure can capture the variables and constants from its surrounding context.
A closure can be used to direct how a collection is sorted.
A handy set of functions exists on collections that you can use to iterate over a collection and transform it. Transforms comprise mapping each element to a new value, filtering out certain values and reducing the collection down to a single value.
Last updated
Was this helpful?