如何用Swift列出选项设置?

时间:2023-01-12 20:34:40

I have a custom OptionSetType struct in Swift. How can I enumerate all values of an instance?

我在Swift中有一个定制的OptionSetType struct。如何枚举实例的所有值?

This is my 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)
}

I would like to something like this:

我想做这样的事情:

let weekdays: WeekdaySet = [.Monday, .Tuesday]
for weekday in weekdays {
    // Do something with weekday
}

3 个解决方案

#1


28  

As of Swift 4, there are no methods in the standard library to enumerate the elements of an OptionSetType (Swift 2) resp. OptionSet (Swift 3, 4).

在Swift 4中,标准库中没有方法枚举OptionSetType (Swift 2) resp的元素。迅速OptionSet(3、4)。

Here is a possible implementation which simply checks each bit of the underlying raw value, and for each bit which is set, the corresponding element is returned. The "overflow multiplication" &* 2 is used as left-shift because << is only defined for the concrete integer types, but not for the IntegerType protocol.

这里有一个可能的实现,它只检查底层原始值的每个比特,对于设置的每个比特,返回相应的元素。“溢出乘法”和* 2被用作左移,因为< <只定义为具体的整数类型,而不是integertype协议。< p>

Swift 2.2:

斯威夫特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
            }
        }
    }
}

Example usage:

使用示例:

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

// Output:
// WeekdaySet(rawValue: 2)
// WeekdaySet(rawValue: 4)

Swift 3:

斯威夫特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
            }
        }
    }
}

Swift 4:

斯威夫特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
            }
        }
    }
}

#2


3  

Based on the previous answers I created a generic Swift 4 solution with IteratorProtocol:

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

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
    }
}

Than in OptionSet extension implement makeIterator()

而在OptionSet扩展中实现makeIterator()

assuming your OptionSets will be Int:

假设您的选项集是Int:

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

Right now every time you create an OptionSet, just conform it to Sequence.

现在,每次创建OptionSet时,只需将它按照序列进行调整。

struct WeekdaySet: OptionSet, Sequence {
    let rawValue: Int

    ...
}

You should now be able to iterate over it:

你现在应该可以遍历它:

let weekdays: WeekdaySet = [.monday, .tuesday]
for weekday in weekdays {
    // Do something with weekday
}

I'd also create a typealias to be explicit on what is used:

我还会创建一个typealias来明确使用什么:

typealias SequenceOptionSet = OptionSet & Sequence

typealias SequenceOptionSet = OptionSet & Sequence

#3


1  

Here you go. I also added a convenience initializer to cut down on some of the boilerplate:

给你。我还添加了一个方便的初始化器来减少一些样板文件:

enum Day: Int {
  case Sun, Mon, Tue, Wed, Thu, Fri, Sat
}

struct WeekdaySet: OptionSetType {

  let rawValue: UInt8

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

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

  static let Sunday = WeekdaySet(1 << 0)
  static let Monday = WeekdaySet(1 << 1)
  static let Tuesday = WeekdaySet(1 << 2)
  static let Wednesday = WeekdaySet(1 << 3)
  static let Thursday = WeekdaySet(1 << 4)
  static let Friday = WeekdaySet(1 << 5)
  static let Saturday = WeekdaySet(1 << 6)
  static let AllDays = [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]

  subscript(indexes: Day...) -> [WeekdaySet] {
    var weekdaySets = [WeekdaySet]()

    for i in indexes {
      weekdaySets.append(WeekdaySet.AllDays[i.rawValue])
    }

    return weekdaySets
  }

}

for weekday in WeekdaySet()[Day.Mon, Day.Tue] {
  print(weekday)
}

#1


28  

As of Swift 4, there are no methods in the standard library to enumerate the elements of an OptionSetType (Swift 2) resp. OptionSet (Swift 3, 4).

在Swift 4中,标准库中没有方法枚举OptionSetType (Swift 2) resp的元素。迅速OptionSet(3、4)。

Here is a possible implementation which simply checks each bit of the underlying raw value, and for each bit which is set, the corresponding element is returned. The "overflow multiplication" &* 2 is used as left-shift because << is only defined for the concrete integer types, but not for the IntegerType protocol.

这里有一个可能的实现,它只检查底层原始值的每个比特,对于设置的每个比特,返回相应的元素。“溢出乘法”和* 2被用作左移,因为< <只定义为具体的整数类型,而不是integertype协议。< p>

Swift 2.2:

斯威夫特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
            }
        }
    }
}

Example usage:

使用示例:

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

// Output:
// WeekdaySet(rawValue: 2)
// WeekdaySet(rawValue: 4)

Swift 3:

斯威夫特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
            }
        }
    }
}

Swift 4:

斯威夫特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
            }
        }
    }
}

#2


3  

Based on the previous answers I created a generic Swift 4 solution with IteratorProtocol:

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

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
    }
}

Than in OptionSet extension implement makeIterator()

而在OptionSet扩展中实现makeIterator()

assuming your OptionSets will be Int:

假设您的选项集是Int:

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

Right now every time you create an OptionSet, just conform it to Sequence.

现在,每次创建OptionSet时,只需将它按照序列进行调整。

struct WeekdaySet: OptionSet, Sequence {
    let rawValue: Int

    ...
}

You should now be able to iterate over it:

你现在应该可以遍历它:

let weekdays: WeekdaySet = [.monday, .tuesday]
for weekday in weekdays {
    // Do something with weekday
}

I'd also create a typealias to be explicit on what is used:

我还会创建一个typealias来明确使用什么:

typealias SequenceOptionSet = OptionSet & Sequence

typealias SequenceOptionSet = OptionSet & Sequence

#3


1  

Here you go. I also added a convenience initializer to cut down on some of the boilerplate:

给你。我还添加了一个方便的初始化器来减少一些样板文件:

enum Day: Int {
  case Sun, Mon, Tue, Wed, Thu, Fri, Sat
}

struct WeekdaySet: OptionSetType {

  let rawValue: UInt8

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

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

  static let Sunday = WeekdaySet(1 << 0)
  static let Monday = WeekdaySet(1 << 1)
  static let Tuesday = WeekdaySet(1 << 2)
  static let Wednesday = WeekdaySet(1 << 3)
  static let Thursday = WeekdaySet(1 << 4)
  static let Friday = WeekdaySet(1 << 5)
  static let Saturday = WeekdaySet(1 << 6)
  static let AllDays = [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]

  subscript(indexes: Day...) -> [WeekdaySet] {
    var weekdaySets = [WeekdaySet]()

    for i in indexes {
      weekdaySets.append(WeekdaySet.AllDays[i.rawValue])
    }

    return weekdaySets
  }

}

for weekday in WeekdaySet()[Day.Mon, Day.Tue] {
  print(weekday)
}