swift高阶函数
map
map接受一个闭包作为参数,作用于数组中的每个元素,闭包返回一个变换后的元素,接着将所有这些变换后的元素组成一个新的数组
let cast = ["Vivien", "Marlon", "Kim", "Karl"]
let lowercaseNames = cast.map { $0.lowercased() }
// 'lowercaseNames' == ["vivien", "marlon", "kim", "karl"]
let letterCounts = cast.map { $0.count }
// 'letterCounts' == [6, 6, 3, 4]
compactMap
解包所有可选选项并丢弃nil值
// 转化为 [Int?] 因为内部元素可能不符合转化规则
let strings = ["1", "2", "fish"]
let a = strings.map {Int($0)}
let b = strings.compactMap {Int($0)}
print(a)
print(b)
// [Optional(1), Optional(2), nil]
// [1,2]
flatMap
过滤nil值,降低数组维度,降低可选项维度
与 map 的区别:
- flatMap 会降低数组维度,过滤nil值,
但是每次操作只执行一种功能,优先执行过滤 nil的操作。
嵌套情况见下:
let arr = [[[1,1,1],2],[3,4], nil, nil]
// 优先处理nil值
let arr1 = arr.flatMap{ $0 }
let arr2 = arr.flatMap{ $0 }.flatMap{ $0 }
print(arr1, arr2, separator: "\n")
// [[[1, 1, 1], 2], [3, 4]]
// [[1, 1, 1], 2, 3, 4]
总结:
- map 和 compactMap 返回的元素不改变原先的类型
- 在嵌套 map 的情况下,flatMap 会将 String 降维至 Character
- 在不是嵌套 map 的情况下
- 若原序列是 String 序列,不指定类型或指定为[String]:flatMap 将元素处理成 Character
- 指定[Any]类型,flatMap 与 map、compactMap 一致,不改变原先类型。
作用于可选对象
map()方法也存在于可选对象上:如果一个可选类型有值,map会获取这个值,经过map的闭包处理变为另外一个值,如果这个可选类型的值为nil,那么不会执行map闭包,而是直接返回nil
// 如果name包含字符串,则map()会将解包,将其转换为“ Hi, name包含的字符串”,然后将整个拼接后的字符串放入一个可选对象中并返回以存储在greeting中。
// 如果name不包含字符串为nil,map()将直接返回nil给greeting。
let name: String? = valueOrNil(id: 97)
let greeting = name.map { "Hi, \($0)" }
print(greeting ?? "Unknown user")
类似的 flatMap() 同样可以作用于可选对象,区别是map闭包不能return nil,而flatmap可以
var value: Double? = 10
var newValue: Double? = value.flatMap { v in
if v < 5.0 {
return nil
}
return v / 5.0
}
// newValue is now Optional(2)
newValue = newValue.flatMap { v in
if v < 5.0 {
return nil
}
return v / 5.0
}
// now it's nil
使用场景:替代三目运算符
一般在使用可选对象时,必须分有值和nil值的情况,运用到三目运算符时肯定会使用强制解包,这样很不优雅。
使用 map 代替,如果可选值为 nil,则会直接返回 nil,不会进入后面的闭包。
// 时间操作
var date: NSDate? = ...
// var formatted: String? = date == nil ? nil : NSDateFormatter().stringFromDate(date!)
var formatted: String? = date.map(NSDateFormatter().stringFromDate)
// 获取String字面量的值
func ageToString(age: Int?) -> String {
//return age == nil ? "Unknown age" : "She is (age!) years old"
return age.map { "She is ($0) years old" } ?? "Unknown age"
}
// 在数组中找到某项
func find(identifier: String) -> Item? {
return items.indexOf({$0.identifier == identifier}).map({items[$0]})
}
filter
可以对数组中的元素按照某种规则进行一次过滤
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
//筛选偶数值
let evens = numbers.filter{$0 % 2 == 0} //等同于
let even = numbers.filter{(num) -> Bool in
num % 2 == 0
}
//筛选奇数值
let odds = numbers.filter{$0 % 2 == 1} //等同于
let odd = numbers.filter{(num) -> Bool in
num % 2 == 1
}
reduce(归纳合并成一个元素)
- reduce(initial, combineClosure): 从第一个初始值开始对其进行combineClosure操作,递归式地将序列中的元素合并为一个元素
- combineClosure:规则闭包,要返回如何将元素合并,有两个参数
- $0 代表累加器,初值等于 initial
- $1 代表遍历数组得到的一个元素
var languages = ["Swift", "OC", "java"]
let r = languages.reduce("_", {$0 + $1}) //等同于
let r = languages.reduce("_") { (result, next) -> String in
print("result: \(result)")
print("next: \(next)")
return result + next
}
print(r)
/**
result: _
next: Swift
result: _Swift
next: OC
result: _SwiftOC
next: java
_SwiftOCjava
*/
print([10, 20, 5].reduce(1, { $0 * $1 }) == 1000) // true
// 极简形式 [10, 20, 5].reduce(1, *)
值的注意的问题
- 仅传入计算符号 "+" "*" 作为一个 combinator 函数是有效的
- 它仅仅是对 lhs(Left-hand side,等式左侧) 和 rhs(Right-hand side,等式右侧) 做计算处理,最后返回结果值
[0, 1, 2, 3, 4].reduce(0, +)
[1, 2, 3, 4].reduce(1, *)
- 性能问题:
使用高阶函数之前多考虑实现方案,通常情况下,map 和 filter 所组成的链式结构会引入性能上的问题,因为它们需要多次遍历你的集合才能最终得到结果值,这种操作往往伴随性能损失。
// 初始序列(即 [0,1,2,3,4])被重复访问了三次之多
[0, 1, 2, 3, 4].map({ $0 + 3}).filter({ $0 % 2 == 0}).reduce(0, combine: +)
// 可用 reduce 完全替换实现,极大提高执行效率:
[0, 1, 2, 3, 4].reduce(0, { (ac, r) in
if (r + 3) % 2 == 0 {
return ac + r + 3
} else {
return ac
}
})
// for-loop 版本
var ux = 0
for i in 0...100000 {
if (i + 3) % 2 == 0 {
ux += (i + 3)
}
}
更多范式
- min:返回列表中的最小项。[1,5,2,9,4].minElement() 方法更胜一筹。
- unique:剔除列表中重复的元素。最好的解决方式是使用集合(Set)
sort
let Arr = [13, 45, 27, 80, 22, 53]
// 完整
let sort1 = Arr.sorted { (a: Int, b: Int) -> Bool in
return a < b
}
// 简略
let sort2 = Arr.sorted { $0 < $1 }
// 极简
let sort3 = Arr.sorted(by: <)
参考文档Swift5 高阶函数