当前位置: 首页 > 知识库问答 >
问题:

Swift 1.2中可选展开中的Map和平面图差异

柳修平
2023-03-14

地图和平面地图的定义都是隐含的,但根据文档,它们的定义(显然)不同:

Func地图(f:@noscape(T)-

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

Func平面地图(f:@noscape(T)-

返回f(self)!iff 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

但即使数字为零,它们也产生了相同的结果 map或flatMap应用于ImplicitlyUnwrappedOptional,它们之间的实际区别是什么?我应该选择哪一个,何时选择?


共有3个答案

邓赤岩
2023-03-14

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))
乌翔
2023-03-14

这在映射闭包具有签名(T)的映射中是不可能的-

这不太正确。在我看来,Martin R的答案并没有完全触及问题的核心,那就是文档没有正确描述地图和平面地图之间的区别。

区别不在于他们采取什么样的收尾方式。每个人都会乐于接受一个生成非可选的闭包或一个生成可选的闭包——不管文档怎么说,也不管它们的声明有什么不同。所有这些表达式都可以编译:

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所做的:它将其展开,从而防止出现双重包装的可选对象:

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

宿嘉庆
2023-03-14

(备注:答案已更新,以反映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)-

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

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

flatMap需要类型为的闭包(已包装)-

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

现在闭包的类型为(Int)-

因此,这些调用之间确实没有区别:

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采用类型为的闭包(已包装)-

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

平面图采用类型的闭包(包装)-

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

此处可以选择变换(包裹)

如果(如您的示例所示)使用不返回可选值的闭包调用了flatMap,那么编译器会自动将其转换为可选值,与映射没有任何区别。

 类似资料:
  • 这两种方法有什么区别:和? 请举个例子。

  • 问题内容: 在Xcode 8发行版中,我发现了一个奇怪的场景。 这是代码, 结果如下: 上面的这些表明,当我为没有显式类型的变量分配一个 隐式解包的optional时 ,该类型将被推断为一个可选类型,而不是它最初的类型,也就是 隐式解开的optional 。 我的Xcode已更新至8。任何人都可以验证Xcode 7.x中的行为吗? 更改是由于Swift版本更改还是Xcode? 问题答案: 这是SE

  • 如果像Apple在Swift编程中所说的隐式展开选项应该总是有一个值,那么为什么不使用非选项来代替呢?我知道非选项不能赋值为nil,但还有其他区别吗?

  • 似乎在Swift 4.1中已弃用。但是在Swift 4.1中有一个新方法正在做同样的事情?使用,您可以转换集合中的每个对象,然后删除任何为nil的项目。 喜欢平面图 像compactMap compactMap也在做同样的事情。 这两种方法有什么区别?为什么苹果决定重命名该方法?

  • 问题内容: 为什么无法对隐式解包的可选变量进行突变? 这是一个重现问题的简短示例: 带整数 问题答案: 更新: Xcode Beta 5中的一个小警告已解决此问题: 该数组可以按预期工作,但是现在看来整数仍然需要显式拆包以允许使用 当前,这仅仅是Optionals的本质(无论是否隐式解包)。unwrap运算符返回一个不变的值。这可能是固定的,或者将来会提供更好的解决方案。 目前唯一的解决方法是将数

  • 在Swift 4.0中,以下代码不可编译: 现在我想象是