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))