文章目录
  1. 1. 文档更新说明
  2. 2. 前言
  3. 3. Map
  4. 4. flatMap
  5. 5. compactMap
  6. 6. reduce
  7. 7. filter
  8. 8. 实操

文档更新说明

  • 最后更新 2019年02月26日
  • 首次更新 2019年02月26日

前言

  写这篇文章的时候Swift最新版本是4.2, 网上也有很多源码分析不过已经过时了, 毕竟Swift更新太快了. 本文针对Swift4.2源码做一下高阶函数分析, 如果你一直混淆这几个高阶函数的作用, 或者还不知道他们的作用, 我非常推荐你阅读本文, 这应该是你学习这些新知识最完美的方法了.

Map

  源码位置: https://github.com/apple/swift/blob/master/stdlib/public/core/Sequence.swift

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@inlinable
public func map<T>(
_ transform: (Element) throws -> T
) rethrows -> [T] {
let initialCapacity = underestimatedCount
var result = ContiguousArray<T>()
result.reserveCapacity(initialCapacity)

var iterator = self.makeIterator()

// Add elements up to the initial capacity without checking for regrowth.
for _ in 0..<initialCapacity {
result.append(try transform(iterator.next()!))
}
// Add remaining elements, if any.
while let element = iterator.next() {
result.append(try transform(element))
}
return Array(result)
}

这个源码很简单, 看最后的实操即可, 没什么话说.

flatMap

  源码位置: https://github.com/apple/swift/blob/master/stdlib/public/core/SequenceAlgorithms.swift

1
2
3
4
5
6
7
8
9
10
@inlinable
public func flatMap<SegmentOfResult : Sequence>(
_ transform: (Element) throws -> SegmentOfResult
) rethrows -> [SegmentOfResult.Element] {
var result: [SegmentOfResult.Element] = []
for element in self {
result.append(contentsOf: try transform(element))
}
return result
}

上面可以看到flatMap函数的核心代码就是对每一个元素都执行了 append(contentsOf:)方法, 这个方法可以传入一个数组, 数组的元素都会被平铺到result里面.
最终实际效果就是对二维数组调用flatMap后会返回一个一维数组出来.

compactMap

  源码位置: https://github.com/apple/swift/blob/master/stdlib/public/core/SequenceAlgorithms.swift

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@inlinable // protocol-only
public func compactMap<ElementOfResult>(
_ transform: (Element) throws -> ElementOfResult?
) rethrows -> [ElementOfResult] {
return try _compactMap(transform)
}

// The implementation of flatMap accepting a closure with an optional result.
// Factored out into a separate functions in order to be used in multiple
// overloads.
@inlinable // protocol-only
@inline(__always)
public func _compactMap<ElementOfResult>(
_ transform: (Element) throws -> ElementOfResult?
) rethrows -> [ElementOfResult] {
var result: [ElementOfResult] = []
for element in self {
if let newElement = try transform(element) {
result.append(newElement)
}
}
return result
}

可以看出来上面的核心代码就是 let newElement = try transform(element) 这句, 这句可以把transform闭包返回的nil元素给过滤掉了.

reduce

  源码位置: https://github.com/apple/swift/blob/master/stdlib/public/core/SequenceAlgorithms.swift

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@inlinable
public func reduce<Result>(
_ initialResult: Result,
_ nextPartialResult:
(_ partialResult: Result, Element) throws -> Result
) rethrows -> Result {
var accumulator = initialResult
for element in self {
accumulator = try nextPartialResult(accumulator, element)
}
return accumulator
}

@inlinable
public func reduce<Result>(
into initialResult: __owned Result,
_ updateAccumulatingResult:
(_ partialResult: inout Result, Element) throws -> ()
) rethrows -> Result {
var accumulator = initialResult
for element in self {
try updateAccumulatingResult(&accumulator, element)
}
return accumulator
}

上面源码也是比较简单, 需要注意的是第二个重载函数, 会把运算结果都存在into参数里面去了.下面看实操就比较清楚了.
上面两个重载我们可以看到, Result的类型并不要求一定要和数组元素一样. 这就意味着你可以根据数组元素内容按照闭包的算法, 返回一个想要的其他类型出来.比如下面的例子

1
2
let arr = [1,2,3,4,5]
print( arr.reduce(""){String(format: "%@-%d", $0 ,$1)}) //-1-2-3-4-5

filter

  源码位置: https://github.com/apple/swift/blob/master/stdlib/public/core/Sequence.swift

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@inlinable
public __consuming func filter(
_ isIncluded: (Element) throws -> Bool
) rethrows -> [Element] {
return try _filter(isIncluded)
}

@_transparent
public func _filter(
_ isIncluded: (Element) throws -> Bool
) rethrows -> [Element] {

var result = ContiguousArray<Element>()

var iterator = self.makeIterator()

while let element = iterator.next() {
if try isIncluded(element) {
result.append(element)
}
}

return Array(result)
}

filter方法的核心代码就是if try isIncluded(element)这句了, 如果闭包返回true, 则对应元素可以被加入数组, 否则丢掉.

实操

  下面我们自己来实现一遍这几个高阶函数, 更好理解他们的用法.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
extension Sequence { //这里对Sequence进行扩展而不对Array扩展, 这样也能支持其他遵循Sequence协议的类型, 比如String
func fuckMap<T>(_ transform:(Element) throws -> T) rethrows -> [T] {
var newArray = [T]()
for ele in self {
newArray.append(try transform(ele))
}
return newArray
}

func fuckFlatMap<T : Sequence>(_ transform: (Element) throws -> T) rethrows -> [T.Element] {
var result: [T.Element] = []
for element in self {
result.append(contentsOf: try transform(element))
}
print("func fuckFlatMap<T : Sequence>(_ transform: (Element) throws -> T) rethrows -> [T.Element]")
return result
}

// 这里重载了一下上面的方法, 这样闭包可以返回元素也可以返回数组了, 不过这个用法在Swift4.2已经过期了
func fuckFlatMap<T>(_ transform: (Element) throws -> T) rethrows -> [T] {
var result:[T] = []
for element in self {
result.append(contentsOf: [try transform(element)])
}
print("func fuckFlatMap<T>(_ transform: (Element) throws -> T) rethrows -> [T]")
return result
}

func fuckCompactMap<T>(_ transform: (Element) throws -> T?) rethrows -> [T] {
var result:[T] = []
for ele in self {
if let e = try transform(ele) {
result.append(e)
}
}
return result
}

func fuckReduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result {
var accumulator = initialResult
for element in self {
accumulator = try nextPartialResult(accumulator, element)
}
return accumulator
}

func fuckReduce<Result>(into initialResult: __owned Result, _ updateAccumulatingResult:(inout Result, Element) throws -> ()) rethrows -> Result {
var accumulator = initialResult
for element in self {
try updateAccumulatingResult(&accumulator, element)
}
return accumulator
}

func fuckFilter(_ isInclude:(Element) throws -> Bool) rethrows -> [Element] {
var result:[Element] = []
for ele in self {
if try isInclude(ele) {
result.append(ele)
}
}
return result
}
}

//使用示例
let arr1 = [1,2,3,4,5,6]
print(arr1.fuckMap { $0 * 2 }) //[2, 4, 6, 8, 10, 12]

let arr2 = [[1,2,3], [4,5,6]]
print(arr2.fuckFlatMap{ $0 }) //[1, 2, 3, 4, 5, 6] 调用的是func fuckFlatMap<T : Sequence>(_ transform: (Element) throws -> T) rethrows -> [T.Element]

let arr20 = [1,2,3,4,5,6]
print(arr20.fuckFlatMap{ $0 * 10 }) //[10, 20, 30, 40, 50, 60] 调用的是func fuckFlatMap<T>(_ transform: (Element) throws -> T) rethrows -> [T]

// 测试compactMap方法
let arr3 = ["1","a","2","3","4","5"]
print(arr3.fuckCompactMap{ Int($0) }) //[1, 2, 3, 4, 5]

// 测试Reduce方法
let arr6 = [1,2,3,4,5]
print( arr6.fuckReduce(""){String(format: "%@-%d", $0 ,$1)})

let str2 = "abracadabra"
print(str2.fuckReduce(into: [:]){counts, letter in counts[letter, default: 0] += 1})

let arr7 = [1,2,3,4,5]
print(arr7.fuckFilter{$0 % 2 == 0}) //[2, 4]

完~

文章目录
  1. 1. 文档更新说明
  2. 2. 前言
  3. 3. Map
  4. 4. flatMap
  5. 5. compactMap
  6. 6. reduce
  7. 7. filter
  8. 8. 实操