6

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

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

如果 self == nil,则返回 nil。否则,返回 f(self!)。

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

返回 f(self)!当且仅当 self 和 f(self) 不为零。

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

let number: Int? = 1

let res1 = number.map { $0 + 1 }.map { $0 + 1 }
let res2 = number.flatMap { $0 + 1 }.flatMap { $0 + 1 }

res1 //3
res2 //3

但是即使是,它们也产生了相同的结果number所以我的问题是,如果我申请或申请s nil. ,它们之间的实际区别是什么?我应该选择哪一个而不是另一个,什么时候?mapflatMapImplicitlyUnwrappedOptional

4

4 回答 4

9

备注:答案已更新以反映 Swift 3 及更高版本中的语法变化,例如取消ImplicitlyUnwrappedOptional.)

Optional.map()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 { $0 + 1 }
print(res1) // Optional(2)

number具有类型Int?并且闭包类型被推断为(Int) -> Int. UInt,返回值的类型是Int?numberis not nil,所以它被解包并传递1给闭包。闭包返回2map返回Optional(2)。如果numbernil那么结果将是nil

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

let number: Int? = 1
let res2 = number.flatMap { $0 + 1 }
print(res2) // Optional(2)

flatMap期望闭包 type (Wrapped) -> U?,但{ $0 + 1 }不返回可选项。为了使其编译,编译器将其转换为

let res2 = number.flatMap { return Optional($0 + 1) }

现在闭包有 type (Int) -> Int?,并且又UInt。再次,number被打开并传递给闭包。闭包返回Optional(2),它也是来自的返回值flatMap。如果numbernil 或者如果闭包会返回nil,那么结果将是nil.

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

let res1 = number.map { $0 + 1 }
let res2 = number.flatMap { $0 + 1 }

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

func foo(_ s : String?) -> Int? {
    return s.flatMap { Int($0) }
}

print(foo("1")) // Optional(1)
print(foo("x")) // nil (because `Int($0)` 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返回可选项的闭包调用,则编译器会自动将其转换为可选项,并且不再有区别。map

于 2015-04-10T08:57:50.020 回答
8

map()这在映射闭包具有签名的情况下是不可能的(T) -> U

这不太对。在我看来,Martin R 的回答并没有完全解决问题的核心,即文档没有正确描述和之间的map区别flatMap

不同之处在于他们采取什么样的关闭方式。每个人都会很高兴地接受一个产生 nonOptional 的闭包或一个产生 Optional 的闭包——不管文档怎么说,尽管他们的声明有所不同。所有这些表达式都编译:

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 之间的唯一区别。

于 2018-06-16T15:53:26.333 回答
0

flatMap解析嵌套的选项,而map没有。

平面图

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

// output: 1

地图

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

// output: Optional(Optional(1))
于 2021-02-10T02:02:18.533 回答
0

[map vs compactMap vs flatMap]

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- 可以返回 Optional Optional
  • flatMap- 可以扁平化 Optional Optional to Optional
Optional.map { () -> T } -> Optional<T>
Optional.map { () -> Optional<T> } -> Optional<Optional<T>>

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

map:函数的返回值transformed被包装到函数的Optional返回值中map

flatMap: 函数返回值与函数transformed返回值相同flatMap

//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)")
}

[Swift Functor,Applicative,Monad]

于 2021-11-06T20:04:27.190 回答