你如何在Swift中枚举OptionSetType?

时间:2015-08-19 18:25:18

标签: swift swift2 swift3

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

这是我的OptionSetType:

cp -p /from/libfoo.so /target/libfoo.so.tmp
mv -f /target/libfoo.so.tmp /target/libfoo.so

我想这样的事情:

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

4 个答案:

答案 0 :(得分:39)

从Swift 4开始,标准库中没有方法 枚举OptionSetType(Swift 2)的元素。 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
            }
        }
    }
}

示例用法:

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

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

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

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

答案 1 :(得分:9)

根据之前的答案,我使用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
    }
}

比OptionSet扩展实现makeIterator()

假设您的OptionSetInt

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

现在每次创建OptionSet时,只需将其符合Sequence

struct WeekdaySet: OptionSet, Sequence {
    let rawValue: Int

    ...
}

您现在应该可以迭代它了:

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

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

typealias SequenceOptionSet = OptionSet & Sequence

答案 2 :(得分:1)

你走了。我还添加了一个便利初始化程序来减少一些样板:

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

答案 3 :(得分:0)

/// Day rawValues used in WeekdaySet. Day proper names capitalized.
enum Day: UInt8, CaseIterable {
    case Sunday    = 0b00000001
    case Monday    = 0b00000010
    case Tuesday   = 0b00000100
    case Wednesday = 0b00001000
    case Thursday  = 0b00010000
    case Friday    = 0b00100000
    case Saturday  = 0b01000000
    var description: String {
        return "\(self)"
    }
}

/// Seven days of the week represented with binary options.
struct WeekdaySet: OptionSet {
    
    /// WeekdaySet initialized with Day (not with Weekday)
    static let Sunday    = WeekdaySet(.Sunday)
    static let Monday    = WeekdaySet(.Monday)
    static let Tuesday   = WeekdaySet(.Tuesday)
    static let Wednesday = WeekdaySet(.Wednesday)
    static let Thursday  = WeekdaySet(.Thursday)
    static let Friday    = WeekdaySet(.Friday)
    static let Saturday  = WeekdaySet(.Saturday)
    
    /// WeekdaySet initialized with Weekday (not with Day)
    static let all: WeekdaySet = [.Sunday, .Monday, .Tuesday, .Wednesday, .Thursday, .Friday, .Saturday]
    static let weekend: WeekdaySet = [.Saturday, .Sunday]
    static let midweek: WeekdaySet = [.Tuesday, .Wednesday, .Thursday]
    static let humpday: WeekdaySet = .Wednesday
    
    /// OptionSet conformance
    let rawValue: UInt8
    init(rawValue: UInt8) {
        self.rawValue = rawValue
    }
    
    /// Init using the enum Day
    init (_ day: Day) {
        self.rawValue = day.rawValue
    }
    
}

extension WeekdaySet: CaseIterable {

    static var allCases: [WeekdaySet] {
        [
            .Sunday,
            .Monday,
            .Tuesday,
            .Wednesday,
            .Thursday,
            .Friday,
            .Saturday,
        ]
    }
    
    /// Computed instance property to filter static allCases
    var array: [WeekdaySet] {
        get {
            return WeekdaySet.allCases.filter { self.contains($0) }
        }
    }
}

extension WeekdaySet: Sequence {
 
    typealias Iterator = AnyIterator<WeekdaySet>
    
    func makeIterator() -> Iterator {
        var iterator = array.makeIterator()
        return AnyIterator {
            return iterator.next()
        }
    }
}

extension WeekdaySet: CustomStringConvertible {
    var description: String {
        if self.array.count < 2 {
            return Day(rawValue: self.rawValue)?.description ?? ""
            
        }
        var results: [String] = []
        for weekday in self.array {
            results.append(Day(rawValue: weekday.rawValue)?.description ?? "")
        }
        return String(describing: results)
    }
}

/// A example set of weekdays
let customSet: WeekdaySet = [.Monday, .Tuesday]

/// Example usages:
print("Does the example set contain humpday?", customSet.contains(.humpday))

for weekday in customSet {
    print("Is \(weekday) midweek?", WeekdaySet.midweek.contains(weekday))
}

print("Thursday:", WeekdaySet.Thursday)
print("Weekend names:", WeekdaySet.weekend)
print("All names", WeekdaySet.all)

// Printed results:
// Does the example set contain humpday? false
// Is Monday midweek? false
// Is Tuesday midweek? true
// Thursday: Thursday
// Weekend names: ["Sunday", "Saturday"]
// All names ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]