2014-11-01 101 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