记录一下Swift中的map和flatMap。
网上很多文章都是之前的,那时flatMap的定义还有两个重载的函数,对应的源码也是之前版本的~因此我在没看源码前,对flatMap的实现有了一些误解。由下面这段代码的输出引发的本次探索。
let arr = [1, 2, 3]
print(arr.map{"NO.\($0)"})
print(arr.flatMap{"NO.\($0)"})
数组支持的map和flatMap实现
map
先贴源码
@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)
}
这段源码和网上大多文章一样,map里面的处理如下。
- 创建一个新的数组
- 遍历数组处理闭包函数,并将结果添加到数组中
- 返回该数组
flatMap
结合我们从Xcode中看到的解释以及源码,我们可以知道flatMap就是返回的Array(s.map(transform).joined())
public func flatMap<SegmentOfResult>(
_ transform: @escaping (Elements.Element) -> SegmentOfResult
) -> LazySequence<
FlattenSequence<LazyMapSequence<Elements, SegmentOfResult>>> {
return self.map(transform).joined()
}
所以,之前的实现方法appendContents那种都无效了。因此开头那个demo输出也和之前不一样了(按之前的实现,flatMap输出的和map一样),结果如下。
["NO.1", "NO.2", "NO.3"]
["N", "O", ".", "1", "N", "O", ".", "2", "N", "O", ".", "3"]
用flatMap的时候,结果返回有可选值的时候,比如对数组里面的字符串转成Int,Xcode会提示我们用compactMap方法。
Optional支持的map和flatMap实现
我是看的唐巧大神的文章Swift 烧脑体操(四) - map 和 flatMap
自己也整理了一下。 源码地址
@inlinable
public func map<U>(
_ transform: (Wrapped) throws -> U
) rethrows -> U? {
switch self {
case .some(let y):
return .some(try transform(y))
case .none:
return .none
}
}
@inlinable
public func flatMap<U>(
_ transform: (Wrapped) throws -> U?
) rethrows -> U? {
switch self {
case .some(let y):
return try transform(y)
case .none:
return .none
}
}
这段代码,乍一看相同,不同之处在于:
- flatMap的tranform返回的是U?,调用的结果return try transform(y)
- map返回的是U,调用的结果return .some(try transform(y))
所以map有可能造成多重Optional的问题,Optional嵌套可看唐巧大神的文章 https://blog.devtang.com/2016/02/27/swift-gym-1-nested-optional/。