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

Swift 1.2中可选展开中的Map和flatMap差异,swift,dictionary,bind,optional,flatmap,Swift,Dictionary,Bind,Optional,Flatmap,map和flatMap均在上定义,但根据文档,它们的定义(显然)不同: func地图(f:@noescape(T)->U)->U 如果self==nil,则返回nil。否则,返回f(self!) func平面图(f:@noescape(T)->U!)->U 返回f(self)!iff self和f(self)不是零 我试着用一个简单的例子来使用它们: let number: Int? = 1 let res1 = number.map { $0 + 1 }.map { $0 + 1 } let

map
flatMap
均在
上定义,但根据文档,它们的定义(显然)不同:

func地图(f:@noescape(T)->U)->U

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

func平面图(f:@noescape(T)->U!)->U

返回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
但即使
number
nil,它们也产生了相同的结果。
因此,我的问题是,如果我将
map
flatMap
应用于
隐式包装
s,它们之间的实际区别是什么?我应该选择哪一个以及何时选择?

(备注:答案已更新,以反映Swift 3及更高版本中的语法变化,例如取消了
隐式附加的

和声明如下(我省略了此处不相关的抛出/收回修饰符):

number
的类型为
Int?
,闭包类型推断为
(Int)->Int
U
Int
,返回值的类型是
Int?
number
不是
nil
,因此它被展开并传递给闭包
1
。闭包返回
2
map
返回
Optional(2)
。如果
number
nil
,则结果为
nil

现在我们考虑你的第二个例子的简化版本:“平面地图”:

let number: Int? = 1
let res2 = number.flatMap { $0 + 1 }
print(res2) // Optional(2)
flatMap
需要类型为
(Wrapped)->U?
的闭包,但
{$0+1}
不返回可选的闭包。为了使其可编译,编译器将其转换为

let res2 = number.flatMap { return Optional($0 + 1) }
现在闭包的类型是
(Int)->Int?
,而
U
又是
Int
。同样,
number
被展开并传递给闭包。闭包返回
Optional(2)
,这也是
flatMap
的返回值。如果
number
nil
,或者如果闭包返回
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))
Optional<Wrapped>.none          --> Optional<U>.none
Optional<Wrapped>.some(wrapped) --> transform(wrapped)
这里的
transform(wrapped)
可以是
可选的。也没有

如果(在您的示例中)调用
flatMap
时使用的闭包不返回可选值,那么编译器会自动将其转换为可选值,并且
map
不再有任何区别

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

那不太对。在我看来,Martin R的回答并不是问题的核心,这就是文档没有正确描述
map
flatMap
之间的区别

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

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

flatMap
解析嵌套选项,而
map
不解析嵌套选项

平面图

var-temp:Int?=3.
变量标志:Bool=false
打印(临时平面图{$0<5?1:nil}???.0)
//产出:1
地图

var-temp:Int?=3.
变量标志:Bool=false
打印(临时映射{$0<5?1:nil}???.0)
//输出:可选(可选(1))

不过,
map
flatMap
之间的区别确实很难区分,因为它们对所采用的闭包类型并不严格。在许多情况下,它们可以采用相同的闭包并产生相同的结果。如果闭包没有生成可选项,
flatMap
可能会阻止它。
Optional.map()
可以返回
nil
,因为
U
是一个泛型类型,可以是您想要的任何类型,包括可选项。@PeterSchorn:是的,只要涉及到嵌套的可选项,
nil
就可以有不同的含义。我试图重写答案,以避免含糊不清。当然,
U
也可以是可选类型。你的答案应该是可以接受的。我的解释有点不同。flatMap采用类型为
(T)->U?
的闭包,即闭包始终返回可选的。在第二个示例中,编译器将闭包
{{inhello}
转换为
{inoptional(“hello”)}
,因此它与第四个示例相同。在这两种情况下,
Optional(“hello”)
返回,没有展开。是的,
flatMap
实际上没有展开闭包返回的可选内容;不同之处在于,它不会将闭包的结果包装在另一个可选项中,而
map
会包装。这就是你的答案没有提到的方法之间的根本区别。
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
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
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))