实现枚举ForwardIndexType

时间:2014-11-01 07:04:13

标签: swift enums

我一直在努力为枚举正确实施ForwardIndexType协议,特别是最终案例的处理(即没有后继的最后一项)。 Swift语言书中没有涵盖这个协议。

这是一个简单的例子

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

在执行for循环中的语句之前,Swift莫名其妙地调用范围结束值的successor()函数。如果范围保持半开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的循环迭代中存在一个错误;它不应该在封闭范围的结束值上调用successor()。其次,ForwardIndexType应该能够返回一个可选的,以便能够发出某个值没有继承者的信号。

有没有人在这个协议上取得更多成功?

3 个答案:

答案 0 :(得分:2)

实际上,似乎successor将在最后一个值上调用。

您可能希望file a bug,但要解决此问题,您只需添加一个标记值即可作为后继者。

答案 1 :(得分:2)

似乎是...运营商

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

调用maximum.successor()。它构建Range<T>之类的

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

因此,如果您想将enum用作Range.Index,则必须定义最后一个值的 next

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

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

答案 2 :(得分:2)

这是一个古老的问题,但我想总结一些事情,并发布另一个可能的解决方案。

由于@jtbandes和@rintaro已经声明使用start...end运算符创建的封闭范围是在内部使用start..<end.successor()创建的 Afaik这是Swift中的故意行为。

在许多情况下,您还可以使用Interval来考虑使用Range或默认情况下Swift声明范围的地方。这里的要点是 interval 不是集合。

因此间隔

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

=====

对于以下内容,我假设上面的代码片段只是一个交叉检查值的调试案例。

要声明间隔,您需要明确指定类型。 HalfOpenInterval (..<)ClosedInterval (...)

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

这需要您进行枚举Comparable。虽然Int已经Comparable,但仍需要将其添加到继承列表

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