@TOC
swift常用高阶函数
swift中比较常用的高阶函数有:map、flatMap、filter、reduce
更多swift相关教程可以参考极客学院文章
1. map
map可以对数组中的每一个元素做一次处理
- 先通过下面实例代码了解一下map的用法
// 计算字符串的长度
let stringArray = ["Objective-C", "Swift", "HTML", "CSS", "JavaScript"]
func stringCount(string: String) -> Int {
return string.characters.count
}
stringArray.map(stringCount)
stringArray.map({string -> Int in
return string.characters.count
})
// $0代表数组中的每一个元素
stringArray.map{
return $0.characters.count
}
- 我们来查看一下map在swift中的定义
我们看到它可以用在 Optionals 和 SequenceType 上(如:数组、词典等)。
代码:
public enum Optional<Wrapped> : _Reflectable, NilLiteralConvertible {
/// If `self == nil`, returns `nil`. Otherwise, returns `f(self!)`.
@warn_unused_result
public func map<U>(@noescape f: (Wrapped) throws -> U) rethrows -> U?
}
extension CollectionType {
/// Returns an `Array` containing the results of mapping `transform`
/// over `self`.
///
/// - Complexity: O(N).
@warn_unused_result
public func map<T>(@noescape transform: (Self.Generator.Element) throws -> T) rethrows -> [T]
}
源码中的一些关键字在这里介绍一下:
@warn_unused_result:表示如果没有检查或者使用该方法的返回值,编译器就会报警告。
@noescape:表示transform这个闭包是非逃逸闭包,它只能在当前函数map中执行,不能脱离当前函数执行。这使得编译器可以明确的知道运行时的上下文环境(因此,在非逃逸闭包中可以不用写self),进而进行一些优化
- 对 Optionals进行map操作
简要的说就是,如果这个可选值有值,那就解包,调用这个函数,之后返回一个可选值,需要注意的是,返回的可选值类型可以与原可选值类型不一致:
///原来类型: Int?,返回值类型:String?
var value:Int? = 1
var result = value.map { String("result = \($0)") }
/// "Optional("result = 1")"
print(result)
var value:Int? = nil
var result = value.map { String("result = \($0)") }
/// "nil"
print(result)
- 对SequenceType进行map操作
我们可以使用map方法遍历数组中的所有元素,并对这些元素一一进行一样的操作(函数方法)。map方法返回完成操作后的数组。
我们可以对比一下用传统的For-in操作和map操作:
1. 传统写法:
var values = [1,3,5,7]
var results = [Int]()
for var value in values {
value *= 2
results.append(value)
}
//"[2, 6, 10, 14]"
print(results)
//传统的For-in实现起来代码很多,不简介,而且效率没有高阶函数高。
2. 使用map高阶函数
let results = values.map ({ (element) -> Int in
return element * 2
})
//"[2, 6, 10, 14]"
此外还有更加精简的写法:
let results = values.map { $0 * 2 }
//"[2, 6, 10, 14]"
3.
通过上面的代码对比,我们可以看出高阶函数精简写法是多么的优雅,就像写诗一样。下面我们来探究一下怎么就精简这么短小了呢,连return语句都不需要了
- 第一步
由于闭包的函数体很短,所以我们将其改写成一行:
let results = values.map ({ (element) -> Int in return element * 2 })
//"[2, 6, 10, 14]"
- 第二步
由于我们的闭包是作为map的参数传入的,系统可以推断出其参数与返回值,因为其参数必须是(Element) -> Int类型的函数。因此,返回值类型,->及围绕在参数周围的括号都可以被忽略:
let results = values.map ({ element in return element * 2 })
//"[2, 6, 10, 14]"
- 第三步
单行表达式闭包可以通过省略return来隐式返回闭包的结果:
由于闭包函数体只含有element * 2这单一的表达式,该表达式返回Int类型,与我们例子中map所需的闭包的返回值类型一致(其实是泛型),所以,可以省略return。
let results = values.map ({ element in element * 2 })
//"[2, 6, 10, 14]"
- 第四步
参数名称缩写(Shorthand Argument Names),由于Swift自动为内联闭包提供了参数缩写功能,你可以直接使用1,$2...依次获取闭包的第1,2,3...个参数。
如果您在闭包表达式中使用参数名称缩写,您可以在闭包参数列表中省略对其的定义,并且对应参数名称缩写的类型会通过函数类型进行推断。in关键字也同样可以被省略:
let results = values.map ({ $0 * 2 })//$0即代表闭包中的第一个参数。
//"[2, 6, 10, 14]"
- 第五步
尾随闭包,由于我们的闭包是作为最后一个参数传递给map函数的,所以我们可以将闭包表达式尾随:
let results = values.map (){ $0 * 2 }
//"[2, 6, 10, 14]"
如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,您甚至可以把()省略掉:
let results = values.map { $0 * 2 }
//"[2, 6, 10, 14]"
2. flatMap
- flatMap返回后的数组中不存在nil,同时它会把Optional解包
let array = ["Apple", "Orange", "Puple", ""]
let arr1 = array.map { a -> Int? in
let length = a.characters.count
guard length > 0 else { return nil }
return length
}
arr1 // [{some 5}, {some 6}, {some 5}, nil]
let arr2 = array.flatMap { a-> Int? in
let length = a.characters.count
guard length > 0 else { return nil}
return length
}
arr2 // [5, 6, 5]
- flatMap还能把数组中存有数组的数组(二维数组、N维数组)一同打开变成一个新的数组
let array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let arr1 = array.map{ $0 }
arr1 // [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let arr2 = array.flatMap{ $0 }
arr2 // [1, 2, 3, 4, 5, 6, 7, 8, 9]
- flatMap也能把两个不同的数组合并成一个数组,这个合并的数组元素个数是前面两个数组元素个数的乘积
let fruits = ["Apple", "Orange", "Puple"]
let counts = [2, 3, 5]
let array = counts.flatMap { count in
fruits.map ({ fruit in
return fruit + " \(count)"
})
}
array // ["Apple 2", "Orange 2", "Puple 2", "Apple 3", "Orange 3", "Puple 3", "Apple 5", "Orange 5", "Puple 5"]
3. filter
filer:过滤,可以对数组中的元素按照某种规则进行一次过滤
// 筛选出字符串的长度小于10的字符串
let stringArray = ["Objective-C", "Swift", "HTML", "CSS", "JavaScript"]
func stringCountLess10(string: String) -> Bool {
return string.characters.count < 10
}
stringArray.filter(stringCountLess10)
stringArray.filter({string -> Bool in
return string.characters.count < 10
})
// $0表示数组中的每一个元素
stringArray.filter{
return $0.characters.count < 10
}
4. reduce
reduce:计算,可以对数组的元素进行计算
// 将数组中的每个字符串用‘、’拼接
let stringArray = ["Objective-C", "Swift", "HTML", "CSS", "JavaScript"]
func appendString(string1: String, string2: String) -> String {
return string1 == "" ? string2 : string1 + "、" + string2
}
// reduce方法中的第一个参数是初始值
stringArray.reduce("", appendString)
stringArray.reduce("", {(string1, string2) -> String in
return string1 == "" ? string2 : string1 + "、" + string2
})
// $0表示计算后的结果, $1表示数组中的每一个元素
stringArray.reduce("", {
return $0 == "" ? $1 : $0 + "、" + $1
})
参考博客,教程:
http://wiki.jikexueyuan.com/project/swift/chapter2/07_Closures.html