Swift 快速,可选展开,在条件允许时反转

Swift 快速,可选展开,在条件允许时反转,swift,error-handling,optional,Swift,Error Handling,Optional,假设我有一个返回可选值的函数。错误时为零,成功时为值: func foo() -> Bar? { ... } 我可以使用以下代码来使用此函数: let fooResultOpt = foo() if let fooResult = fooResultOpt { // continue correct operations here } else { // handle error } 但是,对于任何非平凡的代码,这种方法几乎没有问题: 最后执行了错误处理,很容易遗漏某些

假设我有一个返回可选值的函数。错误时为零,成功时为值:

func foo() -> Bar? { ... }
我可以使用以下代码来使用此函数:

let fooResultOpt = foo()
if let fooResult = fooResultOpt {
    // continue correct operations here
} else {
    // handle error
}
但是,对于任何非平凡的代码,这种方法几乎没有问题:

  • 最后执行了错误处理,很容易遗漏某些内容。当错误处理代码跟在函数调用后面时,情况要好得多

  • 正确的操作代码缩进一级。如果我们要调用另一个函数,我们必须再缩进一次

  • 使用C通常可以编写如下内容:

    Bar *fooResult = foo();
    if (fooResult == null) {
        // handle error and return
    }
    // continue correct operations here
    
    enum Result<T> {
        case Value(T)
        case Error(MyErrorType)
    }
    
    我发现有两种方法可以使用Swift实现类似的代码风格,但我不喜欢这两种方法

    let fooResultOpt = foo()
    if fooResult == nil {
        // handle error and return
    }
    // use fooResultOpt! from here
    let fooResult = fooResultOpt! // or define another variable
    
    如果我到处都写“!”的话,那对我的品味就不好了。我可以引入另一个变量,但看起来也不太好。理想情况下,我希望看到以下内容:

    if !let fooResult = foo() {
        // handle error and return
    }
    // fooResult has Bar type and can be used in the top level
    
    func foo(i:Int) ->Int? {
        switch i {
        case 0: return 0
        case 1: return 1
        default: return nil
        }
    }
    
    var error:Int {
        println("Error")
       return 99
    }
    
    for i in 0...2 {
        var bob:Int = foo(i) ?? error
        println("\(i) produces \(bob)")
    }
    

    我是否遗漏了规范中的某些内容,或者是否有其他方法来编写美观的Swift代码?

    您的假设是正确的,Swift中没有“如果让则否定”语法

    我怀疑其中一个原因可能是语法的完整性。在整个Swift(通常在其他C语言中)中,如果您有一条语句可以绑定本地符号(即命名新变量并给它们赋值),并且可以有一个块体(例如if、while、for),那么这些绑定的作用域就是所述块。相反,让block语句将符号绑定到其封闭范围是不一致的


    尽管如此,这仍然是一件值得考虑的事情——我会推荐并看看苹果对此做了些什么

    这就是模式匹配的全部内容,也是用于此项工作的工具:

    let x: String? = "Yes"
    
    switch x {
    case .Some(let value):
      println("I have a value: \(value)")
    case .None:
      println("I'm empty")
    }
    

    if-let
    表单只是在您不需要两条腿时的一种方便。

    如果您编写的是一组执行相同转换序列的函数,例如在处理REST调用返回的结果时(检查响应是否为零、检查状态、检查应用程序/服务器错误、解析响应等),我要做的是创建一个管道,在每个步骤转换输入数据,最后返回
    nil
    或某一类型的转换结果

    我选择了
    >>
    自定义运算符,它直观地指示数据流,但当然可以自由选择自己的运算符:

    infix operator >>> { associativity left }
    
    func >>> <T, V> (params: T?, next: T -> V?) -> V? {
        if let params = params {
            return next(params)
        }
        return nil
    }
    
    我会这样使用:

    let res: Int? = [1, 2, 3] >>> sumArray >>> powerOf2 >>> module5 >>> sum
    
    此表达式的结果为nil或管道的最后一个函数中定义的类型的值,在上面的示例中为
    Int

    如果需要更好地处理错误,可以定义如下枚举:

    Bar *fooResult = foo();
    if (fooResult == null) {
        // handle error and return
    }
    // continue correct operations here
    
    enum Result<T> {
        case Value(T)
        case Error(MyErrorType)
    }
    
    枚举结果{
    案例值(T)
    案例错误(MyErrorType)
    }
    

    并将上述函数中的所有选项替换为
    Result
    ,返回
    Result.Error()
    ,而不是
    nil
    ,我找到了一种比其他方法看起来更好的方法,但它以不推荐的方式使用了语言功能

    使用问题代码的示例:

    let fooResult: Bar! = foo();
    if fooResult == nil {
        // handle error and return
    }
    // continue correct operations here
    
    fooResult可以用作普通变量,不需要使用“?”或“!”后缀

    苹果的文档说:

    当一个可选项的值在第一次定义该可选项之后立即被确认存在时,隐式展开的可选项是有用的,并且可以肯定地假设该可选项在此后的每一点上都存在。Swift中隐式展开选项的主要用途是在类初始化期间,如无主引用和隐式展开可选属性中所述


    那么以下内容如何:

    if !let fooResult = foo() {
        // handle error and return
    }
    // fooResult has Bar type and can be used in the top level
    
    func foo(i:Int) ->Int? {
        switch i {
        case 0: return 0
        case 1: return 1
        default: return nil
        }
    }
    
    var error:Int {
        println("Error")
       return 99
    }
    
    for i in 0...2 {
        var bob:Int = foo(i) ?? error
        println("\(i) produces \(bob)")
    }
    
    结果如下:

    0 produces 0
    1 produces 1
    Error
    2 produces 99
    

    谢谢,但是您的解决方案与语法if(let value=x){…}else{…}没有什么不同,并且具有我在问题中提到的所有缺点。谢谢您的回答,这是一个很好的方法,但在一般情况下,它似乎非常非强制性,很难扩展和修改。它可能非常适合某些情况,在这些情况下,数据流是自然的。是的,它不是一种通用的方式,它来自函数式编程。也许这不是解决您具体问题的正确方法,但我希望您将来会发现它很有用;-)