【问题标题】:Iterating through an Enum in Swift 3.0在 Swift 3.0 中迭代枚举
【发布时间】:2017-05-12 04:17:17
【问题描述】:

我有一个简单的枚举,我想对其进行迭代。为此,我采用了 Sequence 和 IteratorProtocol,如下面的代码所示。顺便说一句,这可以在 Xcode 8 中复制/粘贴到 Playground。

import UIKit

enum Sections: Int {
  case Section0 = 0
  case Section1
  case Section2
}

extension Sections : Sequence {
  func makeIterator() -> SectionsGenerator {
    return SectionsGenerator()
  }

  struct SectionsGenerator: IteratorProtocol {
    var currentSection = 0

    mutating func next() -> Sections? {
      guard let item = Sections(rawValue:currentSection) else {
        return nil
      }
      currentSection += 1
      return item
    }
  }
}

for section in Sections {
  print(section)
}

但是 for-in 循环会生成错误消息“Type 'Sections.Type' does not conform to protocol 'Sequence'”。 协议一致性在我的扩展中;那么,这段代码有什么问题呢?

我知道还有其他方法可以做到这一点,但我想了解这种方法有什么问题。

谢谢。

【问题讨论】:

标签: swift enums sequence enumeration


【解决方案1】:

更新:从 Swift 4.2 开始,您可以简单地添加协议一致性 到CaseIterable,见How to enumerate an enum with String type?。


您可以迭代符合Sequence 的类型的值 协议。因此

for section in Sections.Section0 {
  print(section)
}

会编译并给出预期的结果。但这当然不是 真的是你想要的,因为值的选择是任意的,而且 序列中不需要值本身。

据我所知,没有办法迭代一个类型本身,所以

for section in Sections {
  print(section)
}

编译。这将要求“元类型”Sections.Type 符合 到Sequence。也许有人证明我错了。

你可以做的是定义一个返回序列的类型方法:

extension Sections {
    static func all() -> AnySequence<Sections> {
        return AnySequence {
            return SectionsGenerator()
        }
    }

    struct SectionsGenerator: IteratorProtocol {
        var currentSection = 0

        mutating func next() -> Sections? {
            guard let item = Sections(rawValue:currentSection) else {
                return nil
            }
            currentSection += 1
            return item
        }
    }

}

for section in Sections.all() {
    print(section)
}

【讨论】:

  • 有一个 SE 提案,基本上是您建议的“元类型”;请参阅gist.github.com/andyyhope/2fc5b6bee8ee1346f688 希望它被采用,因为它似乎是一个整体有用的功能,可以避免各种自定义解决方案。现在,我将坚持迭代类型。谢谢!
  • 另一个关于其状态的参考可以在这里找到:github.com/apple/swift-evolution/pull/114
  • 请同时检查this answer.. 它显示了如何为多个版本的 Swift 创建 iterateEnum() 函数。
【解决方案2】:

注意Martin’s solution可以重构为协议:

import Foundation

protocol EnumSequence
{
    associatedtype T: RawRepresentable where T.RawValue == Int
    static func all() -> AnySequence<T>
}
extension EnumSequence
{
    static func all() -> AnySequence<T> {
        return AnySequence { return EnumGenerator() }
    }
}

private struct EnumGenerator<T: RawRepresentable>: IteratorProtocol where T.RawValue == Int {
    var index = 0
    mutating func next() -> T? {
        guard let item = T(rawValue: index) else {
            return nil
        }
        index += 1
        return item
    }
}

然后,给定一个枚举

enum Fruits: Int {
    case apple, orange, pear
}

你拍了协议和类型别名:

enum Fruits: Int, EnumSequence {
    typealias T = Fruits
    case apple, orange, pear
}

Fruits.all().forEach({ print($0) }) // apple orange pear

【讨论】:

    【解决方案3】:

    这看起来简单多了:

    public protocol EnumSequence {
        init?(rawValue: Int)
    }
    
    public extension EnumSequence {
    
        public static var items: [Self] {
            var caseIndex: Int = 0
            let interator: AnyIterator<Self> = AnyIterator {
                let result = Self(rawValue: caseIndex)
                caseIndex += 1
                return result
            }
            return Array(interator)
        }
    }
    

    【讨论】:

      【解决方案4】:

      在上面的解决方案上进行迭代,请参阅下面的协议,该协议可以通过枚举实现以添加 allValues 序列,但也允许在字符串值之间进行转换。

      对于需要支持objective c的类似字符串的枚举非常方便(那里只允许使用int枚举)。

      public protocol ObjcEnumeration: LosslessStringConvertible, RawRepresentable where RawValue == Int {
          static var allValues: AnySequence<Self> { get }
      }
      
      public extension ObjcEnumeration {
          public static var allValues: AnySequence<Self> {
              return AnySequence {
                  return IntegerEnumIterator()
              }
          }
      
          public init?(_ description: String) {
              guard let enumValue = Self.allValues.first(where: { $0.description == description }) else {
                  return nil
              }
              self.init(rawValue: enumValue.rawValue)
          }
      
          public var description: String {
              return String(describing: self)
          }
      }
      
      fileprivate struct IntegerEnumIterator<T: RawRepresentable>: IteratorProtocol where T.RawValue == Int {
          private var index = 0
          mutating func next() -> T? {
              defer {
                  index += 1
              }
              return T(rawValue: index)
          }
      }
      

      举个具体的例子:

      @objc
      enum Fruit: Int, ObjcEnumeration {
          case apple, orange, pear
      }
      

      现在你可以这样做了:

      for fruit in Fruit.allValues {
      
          //Prints: "apple", "orange", "pear"
          print("Fruit: \(fruit.description)")
      
          if let otherFruit = Fruit(fruit.description), fruit == otherFruit {
              print("Fruit could be constructed successfully from its description!")
          }
      }
      

      【讨论】:

        【解决方案5】:

        如果你的枚举是一个基于 Int 的枚举,你可以像这样做一个有效但有点脏的把戏。

        enum MyEnum: Int {
            case One
            case Two
        }
        
        extension MyEnum {
            func static allCases() -> [MyEnum] {
                var allCases = [MyEnum]()
                for i in 0..<10000 {
                    if let type = MyEnum(rawValue: i) {
                        allCases.append(type)
                    } else {
                        break
                    }
                }
                return allCases
            }
        }
        

        然后循环遍历 MyEnum.allCases()..

        【讨论】:

          【解决方案6】:

          只需添加到枚举: static var allTypes: [Sections] = [.Section0, .Section1, .Section2]

          而且你可以:

          Sections.allTypes.forEach { (section) in
                      print("\(section)")
          }
          

          【讨论】:

            猜你喜欢
            • 2017-06-14
            • 2011-02-07
            • 1970-01-01
            • 1970-01-01
            • 2014-06-09
            • 1970-01-01
            • 1970-01-01
            • 2011-10-06
            • 1970-01-01
            相关资源
            最近更新 更多