Swift 1.2 中可选展开的 Map 和 flatMap 差异

Map and flatMap difference in optional unwrapping in Swift 1.2

mapflatMap 都在 ImplicitlyUnwrappedOptional 上定义,但根据文档,它们的定义(显然)不同:

func map(f: @noescape (T) -> U) -> U!

If self == nil, returns nil. Otherwise, returns f(self!).

func flatMap(f: @noescape (T) -> U!) -> U!

Returns f(self)! iff self and f(self) are not nil.

我试着用一个简单的例子来使用它们:

let number: Int? = 1

let res1 = number.map { [=11=] + 1 }.map { [=11=] + 1 }
let res2 = number.flatMap { [=11=] + 1 }.flatMap { [=11=] + 1 }

res1 //3
res2 //3

但即使 numbernil.,他们也产生了相同的结果 所以我的问题是,如果我将 mapflatMap 应用于 ImplicitlyUnwrappedOptional,它们之间的实际区别是什么?我应该选择哪一个而不是另一个?

(备注:答案已更新,以反映Swift3及以后的语法变化,例如ImplicitlyUnwrappedOptional的废除。)

Optional.map() and Optional.flatMap()声明如下(我省略了这里不相关的throws/rethrows修饰符):

func map<U>(_ transform: (Wrapped) -> U) -> U?
func flatMap<U>(_ transform: (Wrapped) -> U?) -> U?

让我们考虑使用“map”的第一个示例的简化版本:

let number: Int? = 1
let res1 = number.map { [=11=] + 1 }
print(res1) // Optional(2)

number 的类型为 Int?,闭包类型推断为 (Int) -> IntUInt,return 值的类型是 Int?number 不是 nil,所以它被解包并传递 1 被传递给闭包。闭包 returns 2map returns Optional(2)。如果 numbernil 那么结果将是 nil.

现在我们考虑使用“flatMap”的第二个示例的简化版本:

let number: Int? = 1
let res2 = number.flatMap { [=12=] + 1 }
print(res2) // Optional(2)

flatMap 期望 (Wrapped) -> U? 类型的闭包,但 { [=38=] + 1 } 不是 return 可选的。为了使其编译,编译器将其转换为

let res2 = number.flatMap { return Optional([=13=] + 1) }

现在闭包的类型为 (Int) -> Int?U 又是 Int。同样,number 被解包并传递给闭包。闭包 returns Optional(2) 也是来自 flatMap 的 return 值。如果 numbernil 如果闭包 return nil 那么结果将是 nil.

所以这些调用之间确实没有区别:

let res1 = number.map { [=14=] + 1 }
let res2 = number.flatMap { [=14=] + 1 }

然而,这不是 flatMap 的目的。一个更现实的例子是

func foo(_ s : String?) -> Int? {
    return s.flatMap { Int([=15=]) }
}

print(foo("1")) // Optional(1)
print(foo("x")) // nil (because `Int([=15=])` returns nil)
print(foo(nil)) // nil (because the argument is nil)

通常,map 采用类型 (Wrapped) -> U 的闭包并转换

Optional<Wrapped>.none          --> Optional<U>.none
Optional<Wrapped>.some(wrapped) --> Optional<U>.some(transform(wrapped))

flatMap 采用类型 (Wrapped) -> U? 的闭包并转换

Optional<Wrapped>.none          --> Optional<U>.none
Optional<Wrapped>.some(wrapped) --> transform(wrapped)

这里transform(wrapped)也可以是Optional<U>.none

如果(如在您的示例中)调用 flatMap 时使用的闭包 not return 是可选的,那么编译器会将其转换为可选的自动,和 map 没有区别了。

This is not possible with map() where the mapping closure has the signature (T) -> U.

这不太正确。在我看来,Martin R 的回答并没有完全触及问题的核心,即文档没有正确描述 mapflatMap 之间的区别。

区别在于不是他们采取什么样的闭包。每个人都会愉快地接受一个产生非可选的闭包或一个产生可选的闭包——不管文档怎么说,尽管他们的声明有所不同。所有这些表达式编译:

let i : Int? = nil
let result1 = i.map {_ in "hello"} // map, closure produces nonOptional
let result2 = i.flatMap {_ in "hello"} // flatMap, closure produces nonOptional
let result3 = i.map {_ in Optional("hello") } // map, closure produces Optional
let result4 = i.flatMap {_ in Optional("hello") } // flatMap, closure produces Optional


好的,那么实际的区别是什么?这是 flatMap 所做的,以防闭包 确实 产生一个 Optional:它打开它,从而防止双重包装 Optional:

let i : Int? = nil
let result1 = i.map {_ in "hello"} // String?
let result2 = i.flatMap {_ in "hello"} // String?
let result3 = i.map {_ in Optional("hello") } // String?? // double-wrapped
let result4 = i.flatMap {_ in Optional("hello") } // String? // not double-wrapped

这就是map和flatMap的区别。

flatMap 解析嵌套的可选值,而 map 不解析。

平面图

var temp: Int? = 3
var flag: Bool = false
print(temp.flatMap { [=10=] < 5 ? 1 : nil } ?? .zero) 

// output: 1

地图

var temp: Int? = 3
var flag: Bool = false
print(temp.map { [=11=] < 5 ? 1 : nil } ?? .zero) 

// output: Optional(Optional(1))

Swift 可选地图 vs flatMap

让我们创建自己的 Optional 类型的简单实现并实现 mapflatMap 函数

enum CustomOptional<T> {
    case none
    case some(T)
    
    public init(_ some: T) {
        self = .some(some)
    }
    
    func map<U>(_ transform: (T) -> U) -> CustomOptional<U> {
        switch self {
        case .some(let value):
            let transformResult: U = transform(value)
            let result: CustomOptional<U> = CustomOptional<U>(transformResult) //<-- force wrap the transformResult
            return result
        case .none:
            return .none
        }
    }
    
    func flatMap<U>(_ transform: (T) -> CustomOptional<U>) -> CustomOptional<U> {
        switch self {
        case .some(let value):
            let transformResult: CustomOptional<U> = transform(value)
            let result: CustomOptional<U> = transformResult
            return result
        case .none:
            return .none
        }
    }
}
  • map - 可以 return 可选 可选
  • flatMap - 可以平 Optional Optional 到 Optional
Optional.map { () -> T } -> Optional<T>
Optional.map { () -> Optional<T> } -> Optional<Optional<T>>

Optional.flatMap { () -> Optional<T> } -> Optional<T>

maptransformed 函数的 returned 值被封装到 map 函数的 returned 值的 Optional

flatMap:returntransformed 函数的值与 returnflatMap 函数

的值相同
//problem

//T == Int, U == CustomOptional<String>
//map<U>(_ transform: (T) -> U) -> CustomOptional<U>
//map<CustomOptional<String>>(_ transform: (Int) -> CustomOptional<String>) -> CustomOptional<CustomOptional<String>>
let result: CustomOptional<CustomOptional<String>> = CustomOptional(1).map { int in
    return CustomOptional("Hello: \(int)")
}

//solution

//T == Int, U == String
//flatMap<U>(_ transform: (T) -> CustomOptional<U>) -> CustomOptional<U>
//flatMap<U>(_ transform: (Int) -> CustomOptional<String>) -> CustomOptional<String>
let result5: CustomOptional<String> = CustomOptional(1).flatMap { int in
    return CustomOptional("Hello: \(int)")
}