如何在Swift 4.2中获得枚举的下一个例子(即写一个循环方法)

rib*_*ynn 5 enums swift

Swift 4.2引入了一种新CaseIterable协议,可自动生成枚举中所有案例的数组属性.
现在我想实现Enum继承的默认方法CaseIterable,可以返回给定案例的下一个案例.如果这种情况是最后一种情况,请返回第一种情况.像一个圆圈.
如果我为特定的Enum写这个,它可以正常工作:

enum Direction: CaseIterable {
  case east, south, west, north

  func next() -> Direction {
    let all = type(of: self).allCases // 1
    if self == all.last! {
      return all.first!
    } else {
      let index = all.firstIndex(of: self)!
      return all[index + 1]
    }
  }
}

print(Direction.east.next()) // south  
print(Direction.north.next()) // east  
Run Code Online (Sandbox Code Playgroud)

但我想对许多Enum实现这个功能.重复复制和粘贴代码并不好(更不用说这个代码对于每个枚举都是完全相同的).
所以我尝试了这个.但出了点问题.
(我建议你将以下代码复制到游乐场,以便更快地理解这个问题):

extension CaseIterable {
  func next() -> Self {
    let all = type(of: self).allCases // 1
    if self == all.last { // 2
      return all.first!
    } else {
      let index = all.firstIndex { (ele) -> Bool in
        self == ele // 3
      }
      return all[index + 1]
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

三点:

  1. all的类型是Self.AllCases,这是一种Collection类型.但是在上面的方法中,它是[Direction].
  2. 第2行出现错误Value of type 'Self.AllCases' has no member 'last' (即使我避免使用last,也无法避免第3行的错误.)
  3. 在第3行,错误是 Binary operator '==' cannot be applied to two 'Self' operands

甚至我使用通用约束,它也是一样的.

func next<T: CaseIterable>(element: T) -> T {...}
Run Code Online (Sandbox Code Playgroud)

有解决方案吗 :)

Mar*_*n R 7

您的方法存在一些问题:

  • Collection协议没有定义last属性.
  • 为了比较==它们必须的元素Equatable.
  • 集合索引不一定是整数,它们必须递增index(after:).

这似乎是一个有效的解决方案(使用Xcode 10.0 beta 2测试):

extension CaseIterable where Self: Equatable {
    func next() -> Self {
        let all = Self.allCases
        let idx = all.index(of: self)!
        let next = all.index(after: idx)
        return all[next == all.endIndex ? all.startIndex : next]
    }
}
Run Code Online (Sandbox Code Playgroud)

例:

enum Direction: CaseIterable {
    case east, south, west, north
}

print(Direction.east.next()) // south
print(Direction.north.next()) // east
Run Code Online (Sandbox Code Playgroud)

备注:

  • 只有没有关联值的枚举才是CaseIterable,而且这些枚举也是Equatable(但编译器本身并未弄清楚).因此Self: Equatable不是一个真正的限制.
  • Self.allCases 可以在Swift 4.2中使用从实例方法访问type属性.
  • 强制解包是安全的,因为我们知道值是一个元素allCases.
  • 你的enum Direction: CaseIterable编译因为具体 enum Direction类型是Equatable,它Direction.allCases是一个Array- 它有整数索引和last属性.


tad*_*ija 7

如果有人有兴趣到两个previousnext的情况下,这里是以前的答案的升级:

extension CaseIterable where Self: Equatable, AllCases: BidirectionalCollection {
    func previous() -> Self {
        let all = Self.allCases
        let idx = all.index(of: self)!
        let previous = all.index(before: idx)
        return all[previous < all.startIndex ? all.index(before: all.endIndex) : previous]
    }

    func next() -> Self {
        let all = Self.allCases
        let idx = all.index(of: self)!
        let next = all.index(after: idx)
        return all[next == all.endIndex ? all.startIndex : next]
    }
}
Run Code Online (Sandbox Code Playgroud)


Pau*_*l B 5

实际上,您也不需要BidirectionalCollection一致性来实现previous案例。

extension CaseIterable where Self: Equatable {
    
    func previous() -> Self {
        let all = Self.allCases
        var idx = all.firstIndex(of: self)!
        if idx == all.startIndex {
            let lastIndex = all.index(all.endIndex, offsetBy: -1)
            return all[lastIndex]
        } else {
            all.formIndex(&idx, offsetBy: -1)
            return all[idx]
        }
    }

    func next() -> Self {
        let all = Self.allCases
        let idx = all.firstIndex(of: self)!
        let next = all.index(after: idx)
        return all[next == all.endIndex ? all.startIndex : next]
    }
    
}
Run Code Online (Sandbox Code Playgroud)

您还可以制作更通用的循环偏移函数:

extension CaseIterable where Self: Equatable  {
    func advanced(by n: Int) -> Self {
        let all = Array(Self.allCases)
        let idx = (all.firstIndex(of: self)! + n) % all.count
        print(idx)
        if idx >= 0 {
            return all[idx]
        } else {
            return all[all.count + idx]
        }
    }
}
Run Code Online (Sandbox Code Playgroud)