2014-11-01 104 views
9

我一直在努力正確實現一個枚舉的ForwardIndexType協議,尤其是處理最終情況(即沒有後繼的最後一個項目)。這個協議並沒有真正涵蓋在Swift語言書中。實現一個枚舉ForwardIndexType

下面是一個簡單的例子

enum ThreeWords : Int, ForwardIndexType { 
    case one=1, two, three 

    func successor() ->ThreeWords { 
      return ThreeWords(rawValue:self.rawValue + 1)! 
    } 
} 

successor()函數將返回下一個枚舉值,除了最後一個元素,在那裏它將失敗,一個例外,因爲那裏是.three

後沒有價值

ForwardTypeProtocol不允許successor()返回一個條件值,所以似乎沒有辦法表明沒有後繼者。

現在,在for循環遍歷枚舉的所有可能值的封閉範圍使用此,一個運行成用於端部的情況下的一個問題:

for word in ThreeWords.one...ThreeWords.three { 
    print(" \(word.rawValue)") 
} 
println() 

//Crashes with the error: 

fatal error: unexpectedly found nil while unwrapping an Optional value 

夫特莫名其妙地調用的successor()功能在執行for循環中的語句之前,範圍的最終值。如果範圍時半開ThreeWords.one..<ThreeWords.three那麼代碼執行正確,打印1 2

如果我修改的後繼函數,以便它不會嘗試創建一個值比.three大這樣

func successor() ->ThreeWords { 
     if self == .three { 
      return .three 
     } else { 
      return ThreeWords(rawValue:self.rawValue + 1)! 
     } 
    } 

然後for循環不會崩潰,但它也錯過了最後一次迭代,打印如同範圍半開一樣1 2

我的結論是swift的for循環迭代有一個bug;它不應該在封閉範圍的最終值上調用successor()。其次,ForwardIndexType應該能夠返回一個可選項,以便能夠發信號表示沒有特定值的後繼者。

有沒有人有這個協議更成功?

回答

2

的確,看起來successor將被調用上一個值。

你不妨file a bug,但要解決這個你可以簡單地添加的警戒值作爲一個繼任者。

2

看來,...操作

func ...<Pos : ForwardIndexType>(minimum: Pos, maximum: Pos) -> Range<Pos> 

調用maximum.successor()。它構建Range<T>

Range(start: minimum, end: maximum.successor()) 

所以,如果你想使用enumRange.Index,你必須定義的最後一個值的下一個

enum ThreeWords : Int, ForwardIndexType { 
    case one=1, two, three 
    case EXHAUST 

    func successor() ->ThreeWords { 
     return ThreeWords(rawValue:self.rawValue + 1) ?? ThreeWords.EXHAUST 
    } 
} 
+1

這吮吸,因爲現在我所有的枚舉switch語句感染所需的默認情況下, – nielsbot 2015-09-23 02:15:37

2

這是一個古老的問題,但我想總結一些事情,並張貼另一種可能的解決方案。

由於@jtbandes和@rintaro已經說明創建與start...end運營商在內部與start..<end.successor() AFAIK這創造了一個封閉的範圍是斯威夫特的故意行爲。

在很多情況下,你也可以使用一個Interval,你想過用Range或者斯威夫特在默認情況下宣佈範圍。這裏的要點是間隔不是集合。

所以這是可能與區間

for word in ThreeWords.one...ThreeWords.three {...} 

================

對於下面我想上面的代碼段只是一個交叉檢查值的調試案例。

要聲明一個間隔,您需要明確指定類型。無論是HalfOpenInterval (..<)ClosedInterval (...)

var interval:ClosedInterval = ThreeWords.one...ThreeWords.four 

這需要你做出枚舉Comparable。雖然IntComparable已經,你仍然需要將其添加到繼承列表

enum ThreeWords : Int, ForwardIndexType, Comparable { 
    case one=1, two, three, four 

    func successor() ->ThreeWords { 
     return ThreeWords(rawValue:self.rawValue + 1)! 
    } 
} 

最後枚舉需要符合Comparable。這是一個通用的方法,因爲你的枚舉也符合協議RawRepresentable

func <<T: RawRepresentable where T.RawValue: Comparable>(lhs: T, rhs: T) -> Bool { 
    return lhs.rawValue < rhs.rawValue 
} 

就像我寫的,你不能在一個循環遍歷它了,但你可以使用一個開關有一個快速的交叉檢查:

var interval:ClosedInterval = ThreeWords.one...ThreeWords.four 
switch(ThreeWords.four) { 
    case ThreeWords.one...ThreeWords.two: 
     print("contains one or two") 
    case let word where interval ~= word: 
     print("contains: \(word) with raw value: \(word.rawValue)") 
    default: 
     print("no case") 
} 

打印「包含:四連原始值:4