美文网首页
函数式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