前言
在学 Swift 的时候,就觉得 Swift 提供的几个高阶函数特别好用,每次有使用场景时,都会查资料看看怎么用。现在不知不觉已经写了一年多 Swift 了,这几个高阶函数也用得很熟练了,但是写文章记录这件事也因为各种工作、生活上的事情一拖再拖。
现在这篇关于高阶函数的介绍,它来了。内容主要是介绍 forEach、filter、map、flatMap、compactMap、reduce、sorted等 7 个函数的作用,以及他们的使用方式。本文可以当做字典按需查阅。
文章目录
高阶函数介绍
1. forEach
forEach
函数的作用 for
- in
基本一致, 它接受一个闭包参数,该闭包接受序列中的一个元素作为参数并执行某些操作。时间复杂度是
O
(
n
)
O(n)
O(n)
函数的形式如下:
func forEach(_ body: (Element) throws -> Void) rethrows
使用例子如下:
let numberWords = ["one", "two", "three"]
// 遍历数组中的结果并打印
numberWords.forEach { word in
print(word)
}
需要注意的是:forEach 函数没有返回值,并且不能在操作函数中改变集合中的元素。
2. filter
filter
函数正如其名,它主要用于过滤数组、集合和字典等类型对象中的元素。它接受一个闭包参数,该闭包返回的结果为 true
,该元素就会保留,否则就会被移除。时间复杂度是
O
(
n
)
O(n)
O(n)
函数的形式如下:
func filter(_ isIncluded: (Element) throws -> Bool) rethrows -> [Element]
下面是一个简单的例子,是利用 filter
函数过滤数组中的偶数,并返回一个新的数组:
let nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let filteredNums = nums.filter { $0 % 2 == 0 }
print(filteredNums) // 输出 [2, 4, 6, 8, 10]
在上面的示例中,闭包表达式 { $0 % 2 == 0 }
接受一个参数 $0
,判断 $0
是否为偶数,如果是则返回 true,否则返回 false。
3. map
map()
函数用于对集合中的每个元素进行转换,并返回一个新的集合,其中包含转换后的元素。时间复杂度是
O
(
n
)
O(n)
O(n)。
函数的形式如下:
func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]
下面是用 map 做数组类型转换的例子:
struct Person {
var name: String
var age: Int
}
// 创建一个Person对象数组
let peoples = [
Person(name: "Alice", age: 30),
Person(name: "Bob", age: 25),
Person(name: "Charlie", age: 35)
]
// [Person] -> [String]
let names = peoples.map { $0.name }
print(names) // 输出:["Bob", "Alice", "Charlie"]
需要注意的是,使用 map
函数不会自动帮我们去掉转换失败的 nil
值,如有相关诉求可以使用 compactMap
函数。
4. flatMap
flatMap
函数和上面提到的 map
函数相似,也是用于对每个元素做转换,返回一个新的集合;但是不太一样的是,它对每个元素转换的返回值可以是集合类型的,然后会将所有的结果集合合并成一个,常用于做数据打平操作(例如将二维数组转一维)。时间复杂度是
O
(
n
+
m
)
O(n + m)
O(n+m),其中
n
n
n 是数组长度,
m
m
m 是结果长度。
函数的形式如下:
func flatMap<SegmentOfResult>(_ transform: (Element) throws -> SegmentOfResult) rethrows -> [SegmentOfResult.Element] where SegmentOfResult : Sequence
下面是利用 flatMap
函数将二维数组转一维的例子:
let numbers = [1, 2, 3, 4]
// [[1], [2, 2], [3, 3, 3], [4, 4, 4, 4]]
let mapped = numbers.map { Array(repeating: $0, count: $0) }
let flatMapped = numbers.flatMap { Array(repeating: $0, count: $0) }
print(flatMapped) // 输出: [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
5. compactMap
compactMap
函数的作用和 map
、flatMap
函数一样,唯一不同的是它会剔除结果集合中转换失败的 nil
值。时间复杂度是
O
(
n
+
m
)
O(n + m)
O(n+m),其中
n
n
n 是数组长度,
m
m
m 是结果长度。
函数的形式如下:
func compactMap<ElementOfResult>(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]
下面是利用 map
和 compactMap
做对象转换的对比例子。
let possibleNumbers = ["1", "2", "three", "///4///", "5"]
let mapped: [Int?] = possibleNumbers.map { str in Int(str) }
print(mapped) // Prints: [1, 2, nil, nil, 5]
// 通过 compactMap 转换,会剔除转换失败产生的 nil 值
let compactMapped: [Int] = possibleNumbers.compactMap { str in Int(str) }
print(compactMapped) // Prints: [1, 2, 5]
6. reduce
reduce
函数主要用于遍历集合类型对象来进行求值,或构造一个新集合类型对象等,时间复杂度是
O
(
n
)
O(n)
O(n)
函数的形式如下:
// 传入闭包有返回值,常用于遍历求值
func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result
// 传入闭包无返回值,常用构建新集合类型对象
func reduce<Result>(into initialResult: Result, _ updateAccumulatingResult: (inout Result, Element) throws -> ()) rethrows -> Result
6.1 对数组求和
let sizes = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
// 利用 reduce 求元素累加
let totalSize = sizes.reduce(0, { $0 + $1 })
print(totalSize) // 输出: 30
如果只是单纯想做累加,或者累乘操作,可以像下面简写成运算符号。
// 利用 reduce 求元素累加
let totalSize = sizes.reduce(0, +)
6.2 求数组最值
let nums = [1, 4, 54, -4, 5]
// 求最小值
let minNum = nums.reduce(Int.max) { partialResult, num in
return min(partialResult, num)
}
print(minNum) // 输出: -4
6.3 构建新集合类型对象
下面是一个利用 reduce
函数构建字典的例子
struct Person {
var name: String
var age: Int
}
// 创建一个Person数组对象
let peoples = [
Person(name: "Alice", age: 30),
Person(name: "Bob", age: 25),
Person(name: "Charlie", age: 35)
]
// 构建一个 key 是 name,value 是 age 的数组
let infoMap = peoples.reduce(into: [String: Int]()) { partialResult, person in
partialResult[person.name] = person.age
}
print(infoMap) //输出: ["Charlie": 35, "Bob": 25, "Alice": 30]
7. sorted
sorted
是 swift 提供的排序函数,时间复杂度是
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
函数的形式如下:
// 对数组原地排序,要数组对象声明为 `var`
mutating func sort(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows
// 对数组排序,不改变原来的数组,返回结果数组
func sorted(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> [Element]
7.1 对基本类型排序
下面是将字符串按字典序从小到大排序例子
let students: Set = ["Kofi", "Abena", "Peter", "Kweku", "Akosua"]
// 排序
let sortedStudents = students.sorted(by: {$0 < $1 })
print(sortedStudents)
// Prints: ["Abena", "Akosua", "Kofi", "Kweku", "Peter"]
还有下面这种简便写法,直接用比较符号表示升降序。
let sortedStudents = students.sorted(by: <)
也可以这样,因为默认传的就是 <
号。
let sortedStudents = students.sorted()
需要注意的是,如果你对自定义类型使用这种简便写法,那么你需要为该类型实现 Comparable
协议,以便排序算法能够正确比较元素的大小和相等性。
7.2 对自定义类型排序
下面是对自定义的 Person
数组对象,按照年龄大小降序排序的例子。
struct Person {
var name: String
var age: Int
}
// 创建一个Person数组对象
let peoples = [
Person(name: "Alice", age: 30),
Person(name: "Bob", age: 25),
Person(name: "Charlie", age: 35)
]
// 通过闭包表明 Person 对象排序规则
let sortedPeoples = peoples.sorted { p1, p2 in
// 按照年龄降序排序
return p1.age > p2.age
}
print(sortedPeoples)
按照前文提到的,如果要对自定义类型使用简便写法(传入的比较的闭包简写成符号),就要让自定义类型实现 Comparable
协议。
// 实现 Comparable 协议
// MARK: - Comparable
extension Person: Comparable {
static func < (lhs: Person, rhs: Person) -> Bool {
return lhs.age < rhs.age
}
}
// 使用简便写法
let sortedPeoples = peoples.sorted(by: >)