2014-10-10 33 views
5

反轉假設我有返回可選的函數。零如果錯誤和值,如果成功:swift,可選展開,如果條件

func foo() -> Bar? { ... } 

我可以使用下面的代碼使用此功能工作:

let fooResultOpt = foo() 
if let fooResult = fooResultOpt { 
    // continue correct operations here 
} else { 
    // handle error 
} 

但是也有一些問題這種方法對於任何非平凡代碼:

  1. 錯誤處理在最後執行,很容易錯過任何東西。當錯誤處理代碼遵循函數調用時,它會好得多。

  2. 正確的操作代碼縮進一級。如果我們有另一個函數可以調用,我們必須再縮進一次。

用C人們通常會寫這樣的事情:

Bar *fooResult = foo(); 
if (fooResult == null) { 
    // handle error and return 
} 
// continue correct operations here 

我發現了兩種方式來實現類似的代碼風格的雨燕,但我也不喜歡。

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 

我錯過了什麼,在說明書或有一些其他的方式來寫好看SWIFT代碼?

回答

2

你的假設是正確的 - 在Swift中沒有「否定的if-let」語法。

我懷疑有一個原因可能是語法完整性。在整個Swift中(並且通常以其他C語言開發的語言),如果你有一個可以綁定局部符號的語句(即命名新的變量並賦予它們值),並且可以有塊體(例如if,while,for),那麼綁定被限制在所述塊中。讓一個塊語句將符號綁定到它的封閉範圍會變得不一致。

雖然這還是一個合理的想法 - 我推薦filing a bug並且看看蘋果是怎麼做的。

2

這是模式匹配是一回事,是指這個工作的工具:

let x: String? = "Yes" 

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

if-let形式就是當你不需要雙腿便利。

+0

謝謝,但你的解決方案不從語法不同,如果(讓值= X){。 ..}其他{...}並具有我在問題中提到的所有缺點。 – vbezhenar 2014-10-13 09:19:46

1

如果您正在編寫的是一組執行相同序列轉換的函數,例如處理REST調用返回的結果(檢查響應不爲零,檢查狀態,檢查應用程序/服務器錯誤,解析響應等),我會做的是創建一個管道,在每個步驟轉換輸入數據,並在最後返回nil或某種類型的轉換結果。

我選擇了>>>運營商定製,在視覺上表示數據流,當然,隨意選擇自己:

infix operator >>> { associativity left } 

func >>> <T, V> (params: T?, next: T -> V?) -> V? { 
    if let params = params { 
     return next(params) 
    } 
    return nil 
} 

操作員是接受某種類型的值作爲輸入的功能,以及將值轉換爲另一種類型的值的閉包。如果該值不爲零,則函數調用閉包,傳遞該值並返回其返回值。如果值爲nil,則運營商返回nil

大概需要一個例子,讓我們假設我有一個整數數組,我想按順序執行以下操作:

  • 總和陣列
  • 的所有元素計算的力量2
  • 除以5,並返回整數部分,剩餘部分
  • 總和上述2號一起

這些是4個功能:

func sumArray(array: [Int]?) -> Int? { 
    if let array = array { 
     return array.reduce(0, combine: +) 
    } 

    return nil 
} 

func powerOf2(num: Int?) -> Int? { 
    if let num = num { 
     return num * num 
    } 
    return nil 
} 

func module5(num: Int?) -> (Int, Int)? { 
    if let num = num { 
     return (num/5, num % 5) 
    } 
    return nil 
} 

func sum(params: (num1: Int, num2: Int)?) -> Int? { 
    if let params = params { 
     return params.num1 + params.num2 
    } 
    return nil 
} 

,這是我會怎樣使用:

let res: Int? = [1, 2, 3] >>> sumArray >>> powerOf2 >>> module5 >>> sum 

該表達式的結果是nil或者作爲在最後函數所定義的類型的值該管道在上例中是Int

如果您需要做更好的錯誤處理,你可以定義一個枚舉這樣的:

enum Result<T> { 
    case Value(T) 
    case Error(MyErrorType) 
} 

Result<T>替換上述功能全部自選,返回Result.Error(),而不是nil

+0

感謝您的回答,這是一個很好的方法,但它似乎非常不必要,很難在一般情況下進行擴展和修改。它可能非常適合於這種數據流很自然的情況。 – vbezhenar 2014-10-13 09:23:14

+0

是的,它不是一種通用的方式,它來自函數式編程。也許這不是解決您的具體問題的正確方案,但我希望您在將來能夠發現它有用;-) – Antonio 2014-10-13 09:26:41

1

我發現一種看起來比替代方式更好的方式,但它以未建議的方式使用語言功能。

使用代碼的問題舉例:

let fooResult: Bar! = foo(); 
if fooResult == nil { 
    // handle error and return 
} 
// continue correct operations here 

fooResult可作爲正常的變量,並將其使用是沒有必要的「?」要麼 」!」後綴。

蘋果的文件說:當一個可選的值被確認可選首先定義後立即存在,絕對可以假設在每個點之後存在

隱含展開自選是有用的。 Swift中隱式展開選項的主要用途是在類初始化期間,如Unowned References和Unviousned Unwrapped Optional Properties中所述。

0

如何如下:

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 
相關問題