我一直在使用Swift中的函数式编程,我并没有很好的方法来理解Optionals章节中介绍的概念的差异.
使用选项时的模式往往是:
if let thing = optionalThing {
return doThing(thing)
}
else {
return nil
}
Run Code Online (Sandbox Code Playgroud)
这个习惯用标准库函数简洁处理 map
map(optionalThing) { thing in doThing(thing) }
Run Code Online (Sandbox Code Playgroud)
然后,本书继续介绍可选绑定的概念,这是我的差异化能力开始分解的地方.
这本书指导我们定义map功能:
func map<T, U>(optional: T?, f: T -> U) -> U?
{
if let x = optional {
return f(x)
}
else {
return nil
}
}
Run Code Online (Sandbox Code Playgroud)
并指导我们定义可选的绑定功能.注意:本书使用了操作符>>=,但我选择使用命名函数,因为它可以帮助我看到相似之处.
func optionalBind<T, U>(optional: T?, f: T -> U?) -> U?
{
if let x = optional {
return f(x)
}
else {
return nil
}
}
Run Code Online (Sandbox Code Playgroud)
这两种方法的实现看起来都与我相同.两者之间的唯一区别是它们采用的函数参数:
map 采用将T转换为U的函数optionalBind 采用将T转换为可选U的函数"嵌套"这些函数调用的结果会伤害我的大脑:
func addOptionalsBind(optionalX: Int?, optionalY: Int?) -> Int?
{
return optionalBind(optionalX) { x in
optionalBind(optionalY) { y in
x + y
}
}
}
func addOptionalsMap(optionalX: Int?, optionalY: Int?) -> Int?
{
return map(optionalX) { x in
map(optionalY) { y in
x + y
}
}
}
Run Code Online (Sandbox Code Playgroud)
addOptionalsBind功能完全符合您的期望.addOptionalsMap函数无法编译说明:
"诠释?" 不能转换为'Int?'
Rob*_*ier 15
- map采用将T转换为U的函数
- optionalBind采用将T转换为可选U的函数
究竟.这就是整个差异.让我们考虑一个非常简单的功能lift().它会转换T成T?.(在Haskell中,将调用该函数return,但对于非Haskell程序员而言,这有点过于混乱,而且return是关键字).
func lift<T>(x: T) -> T? {
return x
}
println([1].map(lift)) // [Optional(1)]
Run Code Online (Sandbox Code Playgroud)
大.现在,如果我们再次这样做:
println([1].map(lift).map(lift)) // [Optional(Optional(1))]
Run Code Online (Sandbox Code Playgroud)
嗯.所以现在我们有一个Int??,这是一个痛苦的处理.我们真的只是有一个级别的可选性.让我们构建一个函数来做到这一点.我们将其称为flatten并且将双选项变为单选项.
func flatten<T>(x: T??) -> T? {
switch x {
case .Some(let x): return x
case .None : return nil
}
}
println([1].map(lift).map(lift).map(flatten)) // [Optional(1)]
Run Code Online (Sandbox Code Playgroud)
真棒.正是我们想要的.你知道,这.map(flatten)发生了很多,所以让我们给它一个名字:( flatMap这就是Scala所谓的语言).几分钟的演奏应该向你证明,实现flatMap()完全是实现,bindOptional他们做同样的事情.选择一个可选的东西,返回一个可选项,然后从中获取一个单独的"optional-ness".
这是一个非常普遍的问题.Haskell有一个内置的运算符(>>=)很常见.如果你使用方法而不是函数,那么Swift 也有一个内置的运算符.它被称为可选链接(Swift并没有将其扩展到函数,这真是一种耻辱,但Swift比它喜欢函数更喜欢方法):
struct Name {
let first: String? = nil
let last: String? = nil
}
struct Person {
let name: Name? = nil
}
let p:Person? = Person(name: Name(first: "Bob", last: "Jones"))
println(p?.name?.first) // Optional("Bob"), not Optional(Optional(Optional("Bob")))
Run Code Online (Sandbox Code Playgroud)
?.真的只是flatMap(*)这真的是公正的bindOptional.为什么不同的名字?好吧,事实证明,"map and then flatten"相当于另一个叫monadic bind的想法,它以不同的方式思考这个问题.是的,单子和所有这些.如果你认为T?它是一个monad(它是),那么flatMap结果证明是必需的绑定操作.(所以"bind"是一个更通用的术语,适用于所有monad,而"flat map"指的是实现细节.我发现"平面地图"更容易教人们,但是YMMV.)
如果您想要更长版本的讨论以及如何将其应用于其他类型Optional,请参阅Flattenin'Wave Mappenin'.
(*).?也可以map取决于你传递的内容.如果你通过T->U?那么它flatMap.如果你通过T->U,你可以把它看成是map,或者你仍然认为它是的flatMap,其中U被隐式提升为U?(斯威夫特会自动完成).
通过更详细的实施可能会更清楚地发生什么addOptionalsMap.让我们从最里面的调用开始 - map而不是你在那里,让我们使用它:
let mappedInternal: Int? = map(optionalY) { (y: Int) -> Int in
return x + y
}
Run Code Online (Sandbox Code Playgroud)
提供的闭包map接受Int和返回Int,而对map自身的调用返回一个可选的:Int?.没有惊喜!让我们向前迈出一步,看看会发生什么:
let mappedExternal: ??? = map(optionalX) { (x: ???) -> ??? in
let mappedInternal: Int? = map(optionalY) { (y: Int) -> Int in
return x + y
}
return mappedInternal
}
Run Code Online (Sandbox Code Playgroud)
在这里我们可以mappedInternal从上面看到我们的价值,但还有一些类型未定义.map有签名(T?, T -> U) -> U?,所以我们只需要找出T并U在这种情况下.我们知道闭包的返回值mappedInternal是一个Int?,所以U就Int?在这里.T另一方面,可以保持非选择性Int.替代,我们得到这个:
let mappedExternal: Int?? = map(optionalX) { (x: Int) -> Int? in
let mappedInternal: Int? = map(optionalY) { (y: Int) -> Int in
return x + y
}
return mappedInternal
}
Run Code Online (Sandbox Code Playgroud)
闭包是T -> U,其结果是Int -> Int?,并且整个map表达式最终映射Int?到Int??.不是你的想法!
与使用optionalBind完全类型指定的版本对比:
let boundExternal: ??? = optionalBind(optionalX) { (x: ???) -> ??? in
let boundInternal: Int? = optionalBind(optionalY) { (y: Int) -> Int? in
return x + y
}
return boundInternal
}
Run Code Online (Sandbox Code Playgroud)
我们来看看???这个版本的那些类型.因为optionalBind我们需要T -> U?关闭,并且具有Int?返回值boundInternal.因此T,U在这种情况下,两者都可以简单Int,并且我们的实现如下所示:
let boundExternal: Int? = optionalBind(optionalX) { (x: Int) -> Int? in
let boundInternal: Int? = optionalBind(optionalY) { (y: Int) -> Int? in
return x + y
}
return boundInternal
}
Run Code Online (Sandbox Code Playgroud)
您的混淆可能来自变量可以作为选项"解除"的方式.使用单个图层时很容易看到:
func optionalOpposite(num: Int?) -> Int? {
if let num = num {
return -num
}
return nil
}
Run Code Online (Sandbox Code Playgroud)
optionalOpposite可以使用类型的变量(Int?如显式期望的)或类型的非可选变量来调用Int.在第二种情况下,非可选变量在调用期间被隐式转换为可选(即,提升).
map(x: T, f: T -> U) -> U?正在提升其回报价值.由于f声明为T -> U,它永远不会返回一个可选项U?.然而,返回值map是U?指f(x)被提升到U?上的回报.
在你的例子中,内部闭包返回x + y,一个Int被提升到一个Int?.然后再次提升该值以Int??导致类型不匹配,因为您已声明addOptionalsMap返回一个Int?.
| 归档时间: |
|
| 查看次数: |
1539 次 |
| 最近记录: |