目前Sequence 上的flatMap(_:) 方法(从 Swift 4 开始)有两种不同的含义:
-
它可以接受一个转换闭包,返回一个可选的T?,它会返回一个[T],过滤掉nil的结果(这个重载is to be renamed到compactMap(_:)在未来的版本中)。
public func flatMap<ElementOfResult>(
_ transform: (Element) throws -> ElementOfResult?
) rethrows -> [ElementOfResult]
-
它可以接受一个返回Sequence 的转换闭包,它会返回一个包含所有结果序列连接的数组。
public func flatMap<SegmentOfResult : Sequence>(
_ transform: (Element) throws -> SegmentOfResult
) rethrows -> [SegmentOfResult.Element]
现在,在 Swift 4 中,String 变成了 RangeReplaceableCollection(因此变成了 Sequence)。所以 Swift 3 代码做到了这一点:
// returns ["foo"], as using the `nil` filtering flatMap, the elements in the closure
// are implicitly promoted to optional strings.
["foo"].flatMap { $0 }
现在这样做:
// returns ["f", "o", "o"], a [Character], as using the Sequence concatenation flatMap,
// as String is now a Sequence (compiler favours this overload as it avoids the implicit
// conversion from String to String?)
["foo"].flatMap { $0 }
为了保持源代码兼容性,专门为字符串flatMap 重载were added:
//===----------------------------------------------------------------------===//
// The following overloads of flatMap are carefully crafted to allow the code
// like the following:
// ["hello"].flatMap { $0 }
// return an array of strings without any type context in Swift 3 mode, at the
// same time allowing the following code snippet to compile:
// [0, 1].flatMap { x in
// if String(x) == "foo" { return "bar" } else { return nil }
// }
// Note that the second overload is declared on a more specific protocol.
// See: test/stdlib/StringFlatMap.swift for tests.
extension Sequence {
@_inlineable // FIXME(sil-serialize-all)
@available(swift, obsoleted: 4)
public func flatMap(
_ transform: (Element) throws -> String
) rethrows -> [String] {
return try map(transform)
}
}
extension Collection {
@_inlineable // FIXME(sil-serialize-all)
public func flatMap(
_ transform: (Element) throws -> String?
) rethrows -> [String] {
return try _flatMap(transform)
}
}
这样,上述用法在 Swift 3 兼容模式下仍会返回 [String],但在 Swift 4 中返回 [Character]。
那么,为什么
let array = [1, 2, 3, 4, 5, 6]
array
.flatMap {
print("DD")
return $0 // Cannot convert return expression of type 'Int' to return type 'String?'
}
.forEach {
print("SS")
print($0)
}
告诉你闭包应该返回一个String??
好吧,Swift 目前不推断多语句闭包的参数和返回类型(有关更多信息,请参阅this Q&A)。因此,flatMap(_:) 重载,其中闭包返回泛型 T? 或泛型 S : Sequence 在没有显式类型注释的情况下无法调用,因为它们需要类型推断来满足泛型占位符。
因此,唯一符合条件的重载是特殊的 String 源兼容性重载,因此编译器期望闭包返回 String?。
要解决此问题,您可以显式注释闭包的返回类型:
array
.flatMap { i -> Int? in
print("DD")
return i
}
.forEach {
print("SS")
print($0)
}
但是,如果您实际上并未在实际代码中使用此 flatMap(_:) 重载的可选过滤功能,则应改用 map(_:)。