你如何在Swift中枚举OptionSetType?

Flo*_*ian 33 swift swift2 swift3

我在Swift中有一个自定义的OptionSetType结构.如何枚举实例的所有值?

这是我的OptionSetType:

struct WeekdaySet: OptionSetType {
    let rawValue: UInt8

    init(rawValue: UInt8) {
        self.rawValue = rawValue
    }

    static let Sunday        = WeekdaySet(rawValue: 1 << 0)
    static let Monday        = WeekdaySet(rawValue: 1 << 1)
    static let Tuesday       = WeekdaySet(rawValue: 1 << 2)
    static let Wednesday     = WeekdaySet(rawValue: 1 << 3)
    static let Thursday      = WeekdaySet(rawValue: 1 << 4)
    static let Friday        = WeekdaySet(rawValue: 1 << 5)
    static let Saturday      = WeekdaySet(rawValue: 1 << 6)
}
Run Code Online (Sandbox Code Playgroud)

我想这样的事情:

let weekdays: WeekdaySet = [.Monday, .Tuesday]
for weekday in weekdays {
    // Do something with weekday
}
Run Code Online (Sandbox Code Playgroud)

Mar*_*n R 41

从Swift 4开始,标准库中没有方法可以枚举OptionSetType(Swift 2)resp 的元素. OptionSet(斯威夫特3,4).

这是一种可能的实现,它简单地检查底层原始值的每个位,并且对于设置的每个位,返回相应的元素."溢出乘法" &* 2用作左移,因为<<仅针对具体的整数类型定义,但不针对IntegerType协议定义.

Swift 2.2:

public extension OptionSetType where RawValue : IntegerType {

    func elements() -> AnySequence<Self> {
        var remainingBits = self.rawValue
        var bitMask: RawValue = 1
        return AnySequence {
            return AnyGenerator {
                while remainingBits != 0 {
                    defer { bitMask = bitMask &* 2 }
                    if remainingBits & bitMask != 0 {
                        remainingBits = remainingBits & ~bitMask
                        return Self(rawValue: bitMask)
                    }
                }
                return nil
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

let weekdays: WeekdaySet = [.Monday, .Tuesday]
for weekday in weekdays.elements() {
    print(weekday)
}

// Output:
// WeekdaySet(rawValue: 2)
// WeekdaySet(rawValue: 4)
Run Code Online (Sandbox Code Playgroud)

斯威夫特3:

public extension OptionSet where RawValue : Integer {

    func elements() -> AnySequence<Self> {
        var remainingBits = rawValue
        var bitMask: RawValue = 1
        return AnySequence {
            return AnyIterator {
                while remainingBits != 0 {
                    defer { bitMask = bitMask &* 2 }
                    if remainingBits & bitMask != 0 {
                        remainingBits = remainingBits & ~bitMask
                        return Self(rawValue: bitMask)
                    }
                }
                return nil
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

斯威夫特4:

public extension OptionSet where RawValue: FixedWidthInteger {

    func elements() -> AnySequence<Self> {
        var remainingBits = rawValue
        var bitMask: RawValue = 1
        return AnySequence {
            return AnyIterator {
                while remainingBits != 0 {
                    defer { bitMask = bitMask &* 2 }
                    if remainingBits & bitMask != 0 {
                        remainingBits = remainingBits & ~bitMask
                        return Self(rawValue: bitMask)
                    }
                }
                return nil
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我几乎不把它称为虫子.OptionSetType不是一个数组,它只是符合ArrayLiteralConvertable协议,这意味着它可以使用数组进行实例化,而无需使用专门的init方法.一旦完成创建,阵列本身就不再相关了; 它已被转换为整数.因此我认为Martin的解决方案非常优雅,与任何使用无符号整数作为原始值的OptionSetType一样工作. (3认同)
  • 谢谢,马丁.您的解决方案看起 但是,我觉得这应该更容易.我向Apple提交了一个错误. (2认同)
  • NP :)我开始阅读更多关于延迟的内容,看起来像NSHipster对这种模式的建议:http://nshipster.com/guard-and-defer/(向下滚动到"(任何其他)推迟认为有害" (2认同)
  • @DavidJames:它使用`RawValue:FixedWidthInteger`编译(并运行).我会相应地更新答案,谢谢你的通知! (2认同)

mil*_*ilo 11

基于之前的答案,我创建了一个通用的Swift 4解决方案IteratorProtocol:

public struct OptionSetIterator<Element: OptionSet>: IteratorProtocol where Element.RawValue == Int {
    private let value: Element

    public init(element: Element) {
        self.value = element
    }

    private lazy var remainingBits = value.rawValue
    private var bitMask = 1

    public mutating func next() -> Element? {
        while remainingBits != 0 {
            defer { bitMask = bitMask &* 2 }
            if remainingBits & bitMask != 0 {
                remainingBits = remainingBits & ~bitMask
                return Element(rawValue: bitMask)
            }
        }
        return nil
    }
}
Run Code Online (Sandbox Code Playgroud)

比在OptionSet扩展工具中 makeIterator()

假设你的OptionSets将是Int:

extension OptionSet where Self.RawValue == Int {
   public func makeIterator() -> OptionSetIterator<Self> {
      return OptionSetIterator(element: self)
   }
}
Run Code Online (Sandbox Code Playgroud)

现在每次创建一个OptionSet时,只需遵守它Sequence.

struct WeekdaySet: OptionSet, Sequence {
    let rawValue: Int

    ...
}
Run Code Online (Sandbox Code Playgroud)

您现在应该可以迭代它:

let weekdays: WeekdaySet = [.monday, .tuesday]
for weekday in weekdays {
    // Do something with weekday
}
Run Code Online (Sandbox Code Playgroud)

我还创建了一个类型,以明确使用的内容:

typealias SequenceOptionSet = OptionSet & Sequence