美文网首页
函数式Swift之Optional

函数式Swift之Optional

作者: 请叫我小陈陈 | 来源:发表于2016-07-29 16:04 被阅读0次

前言

Swift的可选类型可以用来表示可能缺失或是计算失败的值。今天和大家分享的笔记是如何有效地利用可选类型,以及它们在函数式编程范式中的使用方法。

为什么使用可选值

Optional是Swift的一个特色,它解决了“有”和“无”这两个困扰了Objective-C许久的哲学概念,同时代码安全性也得到了很大的增加。

Optional的定义

详情参考http://www.jianshu.com/p/a0bf5aa7f21d

Optional的运用

首先先创建一个存储几个欧洲城市的人口数量的字典:

let cities = ["Paris": 2241, "Madrid": 3165, "Amsterdam": 827, "Berlin": 3562]

然后我们来查询某个城市的人口数量:

//let madridPopulation: Int = cities["Madrid"]//无法通过类型检查
let madridPopulation: Int? = cities["Madrid"]
print(madridPopulation)//Optional(3165)

为什么会无法通过类型检查呢?是因为你不能保证每次查询都会有结果,有可能某Madrid键不存在于cities中,所以我们无法保证每次查询都返回的是Int值。所以说madridPopulation的类型为可选类型。
对于一个可选类型的使用,我们可以检查查询是否成功:

if (madridPopulation != nil) {
    //强制解包
    print("The population of Madrid is \(madridPopulation! * 1000)")
} else {
    print("Unknown city: Madrid")
}

如果查询成功,我们设置了一个运算,这时候出现了一个后缀运算符“!”,该运算符是强制将一个可选类型转换为一个不可选类型,是为了获取可选值中实际的Int值。
在开发的时候,有时候会忘记打"!",虽然编译器会告诉你应该添加一个"!",但是还是挺不方便的。未经检验的可选类型强制解包,解开后发现是nil,这时候你的程序将会崩溃,在控制台就会出现该信息:

fatal error: unexpectedly found nil while unwrapping an Optional value

这种强制解包的做法不推荐大家使用,老司机们可能知道Swift有一个特殊的可选绑定(optional binding)机制,它可以显示地处理异常情况,从而避免运行时错误,但是有个缺点就是比较繁琐,判断比较多。

//可选绑定机制
if let madridPopulation = cities["Madrid"] {
    print("The population of Madrid is \(madridPopulation * 1000)")
} else {
    print("Unknown city: Madrid")
}

Swift还给!运算符提供了一个更安全的替代--??运算符。让我们来看看??运算符的定义吧:

@warn_unused_result
public func ??<T>(optional: T?, @autoclosure defaultValue: () throws -> T) rethrows -> T

@warn_unused_result
public func ??<T>(optional: T?, @autoclosure defaultValue: () throws -> T?) rethrows -> T?

如果大家看起来有点懵,那就上代码吧:

let num1 = 1 ?? 3//num1 = 1
let num2 = nil ?? 3// num2 = 3

就是操作符??左边不为nil的时候将左边的值赋给num,这个时候对于右边的值,编译器是不管的,不去运算的。为什么对于右边的值可以不用管,这要归功于@autoclosure defaultValue: () throws -> T (Swift标准库中定义通过使用 @autoclosure 类型标签来避开创建显示闭包的需求),再仔细的说就是一言不合上代码:

infix operator ?? { associativity right precedence 110 }
func ??<T>(optional: T?, @autoclosure defaultValue: () -> T) -> T {
   if let x = optional {
        return x
    } else {
        return defaultValue()
    }
}

总结就是操作符??左边左边不为nil 就将左边的值赋给num,然后就没有然后了。如果左边为nil,这时候就需要去计算右边的值了,再把右边的值赋给num。

可选链

可选链,它是Swift的一个特殊机制。
考虑下面客户订单的模型的代码片段:

struct Order {
    let orderNumber: Int
    let person: Person?
}

struct Person {
    let name: String
    let address: Address?
}

struct Address {
    let streetName: String
    let city: String
    let state: String?
}

给定一个Order,如何才能查找到客户的状态呢?

let address = Address(streetName: "软件园", city: "成都", state: nil)
let person = Person(name: "小陈陈", address: address)
let order = Order(orderNumber: 2012090301, person: person)

方法一:使用显示解包运算符
print(order.person!.address!.state!)
这种做法不安全,如果中间任意一个数据缺失,就会导致运行异常,比如说这里的state = nil,执行打印语句就会崩溃。
方法二:可选绑定

if let myPerson = order.person{
    if let myAddress = myPerson.address{
        if let myState = myAddress.state{
            print(myState)
        }
    }
}

这种做法相当于方法一是安全多了的,但是貌似太繁琐了,要写的判断很多,如果结构再复杂一点,写的判断就更多了。
方法三:可选链

if let myState = order.person?.address?.state{
    print("This order will be shipped to \(myState)")
}else {
    print("Unknown person, address, or state.")
}

这种做法就是尝试用?运算符去可选类型进行解包,而不是强制将它们解包,当任意一个组成项失败时,整条语句将返回nil。

分支上的可选值

前面我们说的可选绑定机制,有点分支语句的感觉,一步步判断过滤。Swfit还有其他两种分支语句,switch和guard,它们也非常适合与可选值搭配使用。

switch
switch madridPopulation{
    case 0?: print("Nobody in Madrid")
    case (1..<1000)?: print("Less than a million in Madrid")
    case .Some(let x): print("\(x) people in Madrid")
    case .None: print("We don't know about Madrid")
}

这里如果有人对.Some()和.None有点懵的小伙伴,请大家再次去看看Optional的定义(http://www.jianshu.com/p/a0bf5aa7f21d)。
如果大家对一个特定的值木有兴趣的话,也可以直接匹配.Some()和.None。

guard

Swift里面的一个神奇的东西,它的设计旨在当一些条件不满足的时候,可以尽早的退出当前作用域。我们重写一下打印给定城市居民数量的代码:

func populationDescriptionForCity(city: String) -> String? {
    guard let population = cities[city] else { return nil}
    return "The population of Madrid is \(population * 1000)"
}
print(populationDescriptionForCity("Madrid"))//Optional("The population of Madrid is 3165000")
可选映射

从名字我们也可以猜测它是如果可选值是nil,则结果也是nil。不然就执行一些其它运算,类似于:

func incrementOptional(optional: Int?) -> Int? {
    guard let x = optional else {return nil}
    return x + 1
}
flatMap

flatMap函数检查一个可选值是否为nil,若不是,我们将其传递给参数函数,若是nil。那么结果也是nil,我们用链式调用来举个例子:

func populationOfCapital3(country: String) -> Int? {
    return capitals[country].flatMap { capital in
        return cities[capital]
    }.flatMap { population in
        return population * 1000
    }
}

总结

类型系统有助于你捕捉难以察觉的细微错误,其中一些错误很容易在开发过程中被发现,但是会一直留存在代码里面,这样就像是埋来一颗炸弹一样,很不安全。坚持使用可选值能够从根本上杜绝这种错误。

相关文章

网友评论

      本文标题:函数式Swift之Optional

      本文链接:https://www.haomeiwen.com/subject/fqjzjttx.html