如何枚举一个stringtypes的枚举?
enum Suit: String { case spades = "♠" case hearts = "♥" case diamonds = "♦" case clubs = "♣" }
例如,我怎么能做这样的事情:
for suit in Suit { // do something with suit print(suit.rawValue) }
结果示例:
♠ ♥ ♦ ♣
这篇文章在这里是相关的http://www.swift-studies.com/blog/2014/6/10/enumerating-enums-in-swift
基本上提出的解决scheme是
enum ProductCategory : String { case Washers = "washers", Dryers = "dryers", Toasters = "toasters" static let allValues = [Washers, Dryers, Toasters] } for category in ProductCategory.allValues{ //Do something }
我做了一个实用函数iterateEnum()
来迭代任意enum
types的情况。
以下是示例用法:
enum Suit:String { case Spades = "♠" case Hearts = "♥" case Diamonds = "♦" case Clubs = "♣" } for f in iterateEnum(Suit) { println(f.rawValue) }
输出:
♠ ♥ ♦ ♣
但是,这只是为了debugging或testing的目的:这依赖于几个无证电stream(Swift1.1)的编译器行为。 所以,用你自己的风险:)
这里是代码:
func iterateEnum<T: Hashable>(_: T.Type) -> GeneratorOf<T> { var cast: (Int -> T)! switch sizeof(T) { case 0: return GeneratorOf(GeneratorOfOne(unsafeBitCast((), T.self))) case 1: cast = { unsafeBitCast(UInt8(truncatingBitPattern: $0), T.self) } case 2: cast = { unsafeBitCast(UInt16(truncatingBitPattern: $0), T.self) } case 4: cast = { unsafeBitCast(UInt32(truncatingBitPattern: $0), T.self) } case 8: cast = { unsafeBitCast(UInt64($0), T.self) } default: fatalError("cannot be here") } var i = 0 return GeneratorOf { let next = cast(i) return next.hashValue == i++ ? next : nil } }
基本的想法是:
-
enum
内存表示 – 不包括带有关联types的enum
– 仅仅是一个案例索引,当案例数为2...256
,与UInt8
相同,257...65536
,UInt16
等等。 所以,它可能是unsafeBitcast
从相应的无符号整数types。 - 枚举值的
.hashValue
与案例的索引相同。 - 从无效索引
.hashValue
的枚举值的.hashValue值为0
添加:
修改了Swift2并实现了@ Kametrixom的答案
func iterateEnum<T: Hashable>(_: T.Type) -> AnyGenerator<T> { var i = 0 return anyGenerator { let next = withUnsafePointer(&i) { UnsafePointer<T>($0).memory } return next.hashValue == i++ ? next : nil } }
ADDED:修订了Swift3
func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> { var i = 0 return AnyIterator { let next = withUnsafePointer(to: &i) { $0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee } } if next.hashValue != i { return nil } i += 1 return next } }
ADDED:修订了Swift3.0.1
func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> { var i = 0 return AnyIterator { let next = withUnsafeBytes(of: &i) { $0.load(as: T.self) } if next.hashValue != i { return nil } i += 1 return next } }
其他解决scheme的工作,但他们都假设,例如可能的排名和诉讼的数量,或者第一和最后的排名可能是什么。 诚然,在可预见的未来,一副牌的布局可能不会有太大的变化。 但是,总的来说,编写代码尽可能less的假设是更为合适的。 我的解决scheme
我已经添加了一个原始types的西装枚举,所以我可以使用西装(rawValue :)来访问西装的情况下:
enum Suit: Int { case Spades = 1 case Hearts, Diamonds, Clubs func simpleDescription() -> String { switch self { case .Spades: return "spades" case .Hearts: return "hearts" case .Diamonds: return "diamonds" case .Clubs: return "clubs" } } func color() -> String { switch self { case .Spades: return "black" case .Clubs: return "black" case .Diamonds: return "red" case .Hearts: return "red" } } } enum Rank: Int { case Ace = 1 case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten case Jack, Queen, King func simpleDescription() -> String { switch self { case .Ace: return "ace" case .Jack: return "jack" case .Queen: return "queen" case .King: return "king" default: return String(self.rawValue) } } }
下面是卡的createDeck()方法的实现。 初始化(rawValue :)是一个failable初始化程序,并返回一个可选的。 通过在两个报表中展开和检查它的值,不需要假设Rank或Suit的数量:
struct Card { var rank: Rank var suit: Suit func simpleDescription() -> String { return "The \(rank.simpleDescription()) of \(suit.simpleDescription())" } func createDeck() -> [Card] { var n = 1 var deck = [Card]() while let rank = Rank(rawValue: n) { var m = 1 while let suit = Suit(rawValue: m) { deck.append(Card(rank: rank, suit: suit)) m += 1 } n += 1 } return deck } }
以下是如何调用createDeck方法:
let card = Card(rank: Rank.Ace, suit: Suit.Clubs) let deck = card.createDeck()
第二个答案确实有效
于是我偶然发现了一些位和字节,并创build了一个扩展(后来我发现这个扩展和@ printaro的答案非常类似)。 这是可用的:
enum E : EnumCollection { case A, B, C } Array(E.cases()) // [A, B, C]
值得注意的是,它可以在任何枚举上使用(没有关联的值)。 请注意,这对于没有任何情况的枚举不起作用。
放弃
就像@ printaro的回答一样,这段代码使用了枚举的底层表示。 这种表示没有logging,将来可能会改变,这将打破它 – >我不build议在生产中使用。
编辑:这是一年左右,这仍然有效。
代码(Swift 2.2,Xcode 7.3.1)
protocol EnumCollection : Hashable {} extension EnumCollection { static func cases() -> AnySequence<Self> { typealias S = Self return AnySequence { () -> AnyGenerator<S> in var raw = 0 return AnyGenerator { let current : Self = withUnsafePointer(&raw) { UnsafePointer($0).memory } guard current.hashValue == raw else { return nil } raw += 1 return current } } } }
代码(Swift 3,Xcode 8.1)
protocol EnumCollection : Hashable {} extension EnumCollection { static func cases() -> AnySequence<Self> { typealias S = Self return AnySequence { () -> AnyIterator<S> in var raw = 0 return AnyIterator { let current : Self = withUnsafePointer(to: &raw) { $0.withMemoryRebound(to: S.self, capacity: 1) { $0.pointee } } guard current.hashValue == raw else { return nil } raw += 1 return current } } } }
(我不知道为什么我需要typealias
,但编译器抱怨没有它)
(我对这个答案做了很大的修改,查看过去版本的编辑)
您可以通过实现ForwardIndexType
协议来遍历枚举。
ForwardIndexType
协议要求您定义一个successor()
函数来遍历元素。
enum Rank: Int, ForwardIndexType { case Ace = 1 case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten case Jack, Queen, King // ... other functions // Option 1 - Figure it out by hand func successor() -> Rank { switch self { case .Ace: return .Two case .Two: return .Three // ... etc. default: return .King } } // Option 2 - Define an operator! func successor() -> Rank { return self + 1 } } // NOTE: The operator is defined OUTSIDE the class func + (left: Rank, right: Int) -> Rank { // I'm using to/from raw here, but again, you can use a case statement // or whatever else you can think of return left == .King ? .King : Rank(rawValue: left.rawValue + right)! }
在一个打开或closures的范围( ..<
或...
)上迭代将在内部调用successor()
函数,它允许你写这个:
// Under the covers, successor(Rank.King) and successor(Rank.Ace) are called to establish limits for r in Rank.Ace...Rank.King { // Do something useful }
原则上可以这样做,假设您不使用枚举的情况下的原始值赋值:
enum RankEnum: Int { case Ace case One case Two } class RankEnumGenerator : Generator { var i = 0 typealias Element = RankEnum func next() -> Element? { let r = RankEnum.fromRaw(i) i += 1 return r } } extension RankEnum { static func enumerate() -> SequenceOf<RankEnum> { return SequenceOf<RankEnum>({ RankEnumGenerator() }) } } for r in RankEnum.enumerate() { println("\(r.toRaw())") }
更新到Swift 2.2 +
func iterateEnum<T: Hashable>(_: T.Type) -> AnyGenerator<T> { var i = 0 return AnyGenerator { let next = withUnsafePointer(&i) { UnsafePointer<T>($0).memory } if next.hashValue == i { i += 1 return next } else { return nil } } }
它的代码更新为Swift 2.2 form @ Kametrixom的一个小车
对于Swift 3.0+ (非常感谢@Philip )
func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> { var i = 0 return AnyIterator { let next = withUnsafePointer(&i) { UnsafePointer<T>($0).pointee } if next.hashValue == i { i += 1 return next } else { return nil } } }
如果你给枚举一个原始的Int值,它会使循环更容易。
例如,你可以使用anyGenerator
一个发生器来获得一个枚举你的值的生成器:
enum Suit: Int, CustomStringConvertible { case Spades, Hearts, Diamonds, Clubs var description: String { switch self { case .Spades: return "Spades" case .Hearts: return "Hearts" case .Diamonds: return "Diamonds" case .Clubs: return "Clubs" } } static func enumerate() -> AnyGenerator<Suit> { var nextIndex = Spades.rawValue return anyGenerator { Suit(rawValue: nextIndex++) } } } // You can now use it like this: for suit in Suit.enumerate() { suit.description } // or like this: let allSuits: [Suit] = Array(Suit.enumerate())
然而,这看起来像一个相当常见的模式,如果我们可以通过简单地遵从一个协议来使任何枚举types可枚举,那不是很好吗? 那么用Swift 2.0和协议扩展,现在我们可以!
只需将其添加到您的项目中:
protocol EnumerableEnum { init?(rawValue: Int) static func firstValue() -> Int } extension EnumerableEnum { static func enumerate() -> AnyGenerator<Self> { var nextIndex = firstRawValue() return anyGenerator { Self(rawValue: nextIndex++) } } static func firstRawValue() -> Int { return 0 } }
现在,无论何时你创build一个枚举(只要它具有一个Int原始值),你都可以通过遵从该协议来使其enumerable:
enum Rank: Int, EnumerableEnum { case Ace, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King } // ... for rank in Rank.enumerate() { ... }
如果枚举值不以0
开头(默认值),则覆盖firstRawValue
方法:
enum DeckColor: Int, EnumerableEnum { case Red = 10, Blue, Black static func firstRawValue() -> Int { return Red.rawValue } } // ... let colors = Array(DeckColor.enumerate())
最后的Suit类,包括用更标准的CustomStringConvertible协议replacesimpleDescription
,将如下所示:
enum Suit: Int, CustomStringConvertible, EnumerableEnum { case Spades, Hearts, Diamonds, Clubs var description: String { switch self { case .Spades: return "Spades" case .Hearts: return "Hearts" case .Diamonds: return "Diamonds" case .Clubs: return "Clubs" } } } // ... for suit in Suit.enumerate() { print(suit.description) }
编辑:
Swift 3
语法:
protocol EnumerableEnum { init?(rawValue: Int) static func firstRawValue() -> Int } extension EnumerableEnum { static func enumerate() -> AnyIterator<Self> { var nextIndex = firstRawValue() let iterator: AnyIterator<Self> = AnyIterator { defer { nextIndex = nextIndex + 1 } return Self(rawValue: nextIndex) } return iterator } static func firstRawValue() -> Int { return 0 } }
我喜欢这个find这个页面后我放在一起的解决scheme: 在Swift中列表理解
它使用Int raws而不是string,但它避免了键入两次,它允许自定义范围,并且不会硬编码原始值。
enum Suit: Int { case None case Spade, Heart, Diamond, Club static let allRawValues = Suit.Spade.rawValue...Suit.Club.rawValue static let allCases = Array(allRawValues.map{ Suit(rawValue: $0)! }) } enum Rank: Int { case Joker case Two, Three, Four, Five, Six case Seven, Eight, Nine, Ten case Jack, Queen, King, Ace static let allRawValues = Rank.Two.rawValue...Rank.Ace.rawValue static let allCases = Array(allRawValues.map{ Rank(rawValue: $0)! }) } func makeDeck(withJoker withJoker: Bool) -> [Card] { var deck = [Card]() for suit in Suit.allCases { for rank in Rank.allCases { deck.append(Card(suit: suit, rank: rank)) } } if withJoker { deck.append(Card(suit: .None, rank: .Joker)) } return deck }
enum Rank: Int { ... static let ranks = (Rank.Ace.rawValue ... Rank.King.rawValue).map{Rank(rawValue: $0)! } } enum Suit { ... static let suits = [Spades, Hearts, Diamonds, Clubs] } struct Card { ... static func fullDesk() -> [Card] { var desk: [Card] = [] for suit in Suit.suits { for rank in Rank.ranks { desk.append(Card(rank: rank,suit: suit)) } } return desk } }
这个怎么样?
我发现自己做了.allValues
遍及我的代码。 我终于想出了一个简单符合Iteratable
协议的方法,并有一个rawValues()
方法。
protocol Iteratable {} extension RawRepresentable where Self: RawRepresentable { static func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> { var i = 0 return AnyIterator { let next = withUnsafePointer(to: &i) { $0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee } } if next.hashValue != i { return nil } i += 1 return next } } } extension Iteratable where Self: RawRepresentable, Self: Hashable { static func hashValues() -> AnyIterator<Self> { return iterateEnum(self) } static func rawValues() -> [Self.RawValue] { return hashValues().map({$0.rawValue}) } } // Example enum Grocery: String, Iteratable { case Kroger = "kroger" case HEB = "heb" case Randalls = "randalls" } let groceryHashes = Grocery.hashValues() // AnyIterator<Grocery> let groceryRawValues = Grocery.rawValues() // ["kroger", "heb", "randalls"]
这就是我最终要做的。 我认为它在可读性和可维护性方面取得了正确的平衡。
struct Card { // ... static func deck() -> Card[] { var deck = Card[]() for i in Rank.Ace.toRaw()...Rank.King.toRaw() { for suit in [Suit.Spades, .Hearts, .Clubs, .Diamonds] { let card = Card(rank: Rank.fromRaw(i)!, suit: suit) deck.append(card) } } return deck } let deck = Card.deck()
对不起,我的回答是具体到我如何使用这个职位,在我需要做的。 对于那些偶然发现这个问题的人来说,寻找一个在枚举中find一个case的方法,这就是做这件事的方法(Swift 2中的新特性):
编辑:小写camelCase现在是Swift 3枚举值的标准
// From apple docs: If the raw-value type is specified as String and you don't assign values to the cases explicitly, each unassigned case is implicitly assigned a string with the same text as the name of that case. enum Theme: String { case white, blue, green, lavender, grey } func loadTheme(theme: String) { // this checks the string against the raw value of each enum case (note that the check could result in a nil value, since it's an optional, which is why we introduce the if/let block if let testTheme = Theme(rawValue: theme) { // testTheme is guaranteed to have an enum value at this point self.someOtherFunction(testTheme) } }
对于那些想知道枚举枚举的人,在这个页面上给出的答案包括一个包含所有枚举值数组的静态var / let是正确的。 最新的苹果示例代码为tvOS包含这完全相同的技术。
这就是说,他们应该build立一个更方便的语言机制(苹果,你在听吗?)!
在这个线程中有很多很好的解决scheme,但是其中一些非常复杂。 我想尽可能简化。 这是一个解决scheme,可能会或可能不适合不同的需求,但我认为它在大多数情况下运作良好:
enum Number: String { case One case Two case Three case Four case EndIndex func nextCase () -> Number { switch self { case .One: return .Two case .Two: return .Three case .Three: return .Four case .Four: return .EndIndex /* Add all additional cases above */ case .EndIndex: return .EndIndex } } static var allValues: [String] { var array: [String] = Array() var number = Number.One while number != Number.EndIndex { array.append(number.rawValue) number = number.nextCase() } return array } }
迭代:
for item in Number.allValues { print("number is: \(item)") }
在Swift 3中,当底层枚举有{rawValue}时,可以实现{Strideable}协议。 好处是没有像其他一些build议那样创build值的数组,并且标准的Swift“for i in …”语句有效,这使得语法更好。
// "Int" to get rawValue, and {Strideable} so we can iterate enum MyColorEnum : Int, Strideable { case Red case Green case Blue case Black //-------- required by {Strideable} typealias Stride = Int func advanced(by n:Stride) -> MyColorEnum { var next = self.rawValue + n if next > MyColorEnum.Black.rawValue { next = MyColorEnum.Black.rawValue } return MyColorEnum(rawValue: next)! } func distance(to other: MyColorEnum) -> Int { return other.rawValue - self.rawValue } //-------- just for printing func simpleDescription() -> String { switch self { case .Red: return "Red" case .Green: return "Green" case .Blue: return "Blue" case .Black: return "Black" } } } // this is how you use it: for i in MyColorEnum.Red ... MyColorEnum.Black { print("ENUM: \(i)") }
另一个scheme
enum Suit: String { case spades = "♠" case hearts = "♥" case diamonds = "♦" case clubs = "♣" static var count: Int { return 4 } init(index: Int) { switch index { case 0: self = .spades case 1: self = .hearts case 2: self = .diamonds default: self = .clubs } } } for i in 0..<Suit.count { print(Suit(index: i).rawValue) }
枚举有toRaw()和fromRaw()方法,所以如果你的原始值是一个Int,你可以迭代从第一个到最后一个枚举:
enum Suit: Int { case Spades = 1 case Hearts, Diamonds, Clubs func simpleDescription() -> String { switch self { case .Spades: return "spades" case .Hearts: return "hearts" case .Diamonds: return "diamonds" case .Clubs: return "clubs" } } } for i in Suit.Spades.toRaw()...Suit.Clubs.toRaw() { if let covertedSuit = Suit.fromRaw(i) { let description = covertedSuit.simpleDescription() } }
一个问题是,在运行simpleDescription方法之前,您需要testing可选值,因此我们首先将convertedSuit设置为我们的值,然后将一个常量设置为convertedSuit.simpleDescription()
这似乎是一个黑客,但如果你使用原始值,你可以做这样的事情
enum Suit: Int { case Spades = 0, Hearts, Diamonds, Clubs ... } var suitIndex = 0 while var suit = Suit.fromRaw(suitIndex++) { ... }
我使用了计算属性,它返回所有值的数组(感谢http://natecook.com/blog/2014/10/loopy-random-enum-ideas/ )。 然而,它也使用int原始值,但我不需要在单独的属性中重复枚举的所有成员。
更新 Xcode 6.1改变了一点,如何获得枚举成员使用原始值,所以我固定上市。 还修复了错误的第一个原始值的小错误
enum ValidSuits:Int{ case Clubs=0, Spades, Hearts, Diamonds func description()->String{ switch self{ case .Clubs: return "♣︎" case .Spades: return "♠︎" case .Diamonds: return "♦︎" case .Hearts: return "♥︎" } } static var allSuits:[ValidSuits]{ return Array( SequenceOf { () -> GeneratorOf<ValidSuits> in var i=0 return GeneratorOf<ValidSuits>{ return ValidSuits(rawValue: i++) } } ) } }
实验是:实验
为卡片添加一个方法,创build一副完整的卡片组合,每张卡片组合一个等级和套装。
因此,除了添加方法(并且不使用尚未教授的东西)之外,不修改或增强给定的代码,我想出了这个解决scheme:
struct Card { var rank: Rank var suit: Suit func simpleDescription() -> String { return "The \(rank.simpleDescription()) of \(suit.simpleDescription())" } func createDeck() -> [Card] { var deck: [Card] = [] for rank in Rank.Ace.rawValue...Rank.King.rawValue { for suit in Suit.Spades.rawValue...Suit.Clubs.rawValue { let card = Card(rank: Rank(rawValue: rank)!, suit: Suit(rawValue: suit)!) //println(card.simpleDescription()) deck += [card] } } return deck } } let threeOfSpades = Card(rank: .Three, suit: .Spades) let threeOfSpadesDescription = threeOfSpades.simpleDescription() let deck = threeOfSpades.createDeck()
对于代表Int
enum
enum Filter: Int { case salary case experience case technology case unutilized case unutilizedHV static let allRawValues = salary.rawValue...unutilizedHV.rawValue // First to last case static let allValues = allRawValues.map { Filter(rawValue: $0)!.rawValue } }
像这样调用它:
print(Filter.allValues)
打印:
[0,1,2,3,4]
对于表示String
enum
enum Filter: Int { case salary case experience case technology case unutilized case unutilizedHV static let allRawValues = salary.rawValue...unutilizedHV.rawValue // First to last case static let allValues = allRawValues.map { Filter(rawValue: $0)!.description } } extension Filter: CustomStringConvertible { var description: String { switch self { case .salary: return "Salary" case .experience: return "Experience" case .technology: return "Technology" case .unutilized: return "Unutilized" case .unutilizedHV: return "Unutilized High Value" } } }
叫它
print(Filter.allValues)
打印:
[“薪水”,“经验”,“技术”,“未使用”,“未使用的高价值”]
enum Rank: Int { case Ace = 0 case Two, Three, Four, Five, Six, Seve, Eight, Nine, Ten case Jack, Queen, King case Count } enum Suit : Int { case Spades = 0 case Hearts, Diamonds, Clubs case Count } struct Card { var rank:Rank var suit:Suit } class Test { func makeDeck() -> Card[] { let suitsCount:Int = Suit.Count.toRaw() let rankCount:Int = Rank.Count.toRaw() let repeatedCard:Card = Card(rank:Rank.Ace, suit:Suit.Spades) let deck:Card[] = Card[](count:suitsCount*rankCount, repeatedValue:repeatedCard) for i:Int in 0..rankCount { for j:Int in 0..suitsCount { deck[i*suitsCount+j] = Card(rank: Rank.fromRaw(i)!, suit: Suit.fromRaw(j)!) } } return deck } }
根据里克的回答:这是5倍的速度
这是我build议的方法。 这并不完全令人满意(我对Swift和OOP很陌生!)但也许有人可以改进它。 这个想法是让每个枚举提供自己的范围信息,如.first和.last属性。 它为每个枚举添加了两行代码:仍然有点硬编码,但至less不会重复整个集合。 It does require modifying the Suit enum to be an Int like the Rank enum is, instead of untyped.
Rather than echo the whole solution, here's the code I added to the Rank enum, somewhere after the case statements (Suit enum is similar):
var first: Int { return Ace.toRaw() } var last: Int { return King.toRaw() }
and the loop I used to build the deck as an array of String. (The problem definition did not state how the deck was to be structured.)
func createDeck() -> [String] { var deck: [String] = [] var card: String for r in Rank.Ace.first...Rank.Ace.last { for s in Suit.Hearts.first...Suit.Hearts.last { card = Rank.simpleDescription( Rank.fromRaw(r)!)() + " of " + Suit.simpleDescription( Suit.fromRaw(s)!)() deck.append( card) } } return deck }
It's unsatisfactory because the properties are associated to an element rather than to the enum. But it does add clarity to the 'for' loops. I'd like it to say Rank.first instead of Rank.Ace.first. It works (with any element), but it's ugly. Can someone show how to elevate that to the enum level?
And to make it work, I lifted the createDeck method out of the Card struct… could not figure out how to get a [String] array returned from that struct, and that seems a bad place to put such a method anyway.
While dealing with Swift 2.0
here is my suggestion:
I have added the raw type to Suit
enum
enum Suit: Int {
然后:
struct Card { var rank: Rank var suit: Suit func fullDeck()-> [Card] { var deck = [Card]() for i in Rank.Ace.rawValue...Rank.King.rawValue { for j in Suit.Spades.rawValue...Suit.Clubs.rawValue { deck.append(Card(rank:Rank(rawValue: i)! , suit: Suit(rawValue: j)!)) } } return deck } }
As with @Kametrixom answer here I believe returning an array would be better than returning AnySequence, since you can have access to all of Array's goodies such as count, etc.
Here's the re-write:
public protocol EnumCollection : Hashable {} extension EnumCollection { public static func allValues() -> [Self] { typealias S = Self let retVal = AnySequence { () -> AnyGenerator<S> in var raw = 0 return AnyGenerator { let current : Self = withUnsafePointer(&raw) { UnsafePointer($0).memory } guard current.hashValue == raw else { return nil } raw += 1 return current } } return [S](retVal) } }
This is yet another approach based on @Peymmankh's answer, adapted to Swift 3 .
public protocol EnumCollection : Hashable {} extension EnumCollection { public static func allValues() -> [Self] { typealias S = Self let retVal = AnySequence { () -> AnyIterator<S> in var raw = 0 return AnyIterator { let current = withUnsafePointer(to: &raw) { $0.withMemoryRebound(to: S.self, capacity: 1) { $0.pointee } } guard current.hashValue == raw else { return nil } raw += 1 return current } } return [S](retVal) } }
I have used the below method, the assumption is that I know which is the last value in the Rank enum and all the ranks have incremental values after Ace
I prefer this way as it is clean and small, easy to understand
func cardDeck() -> Card[] { var cards: Card[] = [] let minRank = Rank.Ace.toRaw() let maxRank = Rank.King.toRaw() for rank in minRank...maxRank { if var convertedRank: Rank = Rank.fromRaw(rank) { cards.append(Card(rank: convertedRank, suite: Suite.Clubs)) cards.append(Card(rank: convertedRank, suite: Suite.Diamonds)) cards.append(Card(rank: convertedRank, suite: Suite.Hearts)) cards.append(Card(rank: convertedRank, suite: Suite.Spades)) } } return cards }
There is a clever way, and frustrating as it is it illustrates the difference between the two different kinds of enums.
尝试这个:
func makeDeck() -> Card[] { var deck: Card[] = [] var suits: Suit[] = [.Hearts, .Diamonds, .Clubs, .Spades] for i in 1...13 { for suit in suits { deck += Card(rank: Rank.fromRaw(i)!, suit: suit) } } return deck }
The deal is that an enum backed by numbers (raw values) is implicitly explicitly ordered, whereas an enum that isn't backed by numbers is explicitly implicitly unordered.
Eg when we give the enum values numbers, the language is cunning enough to figure out what order the numbers are in. If on the other hand we don't give it any ordering, when we try to iterate over the values the language throws its hands up in the air and goes "yes, but which one do you want to go first???"
Other languages which can do this (iterating over unordered enums) might be the same languages where everything is 'under the hood' actually a map or dictionary, and you can iterate over the keys of a map, whether there's any logical ordering or not.
So the trick is to provide it with something that is explicitly ordered, in this case instances of the suits in an array in the order we want. As soon as you give it that, Swift is like "well why didn't you say so in the first place?"
The other shorthand trick is to use the forcing operator on the fromRaw function. This illustrates another 'gotcha' about enums, that the range of possible values to pass in is often larger than the range of enums. For instance if we said Rank.fromRaw(60) there wouldn't be a value returned, so we're using the optional feature of the language, and where we start using optionals, forcing will soon follow. (Or alternately the if let construction which still seems a bit weird to me)
It took me a little more then just one method in the struct like the swift book called for but i set up next functions in the enum. I would have used a protocol I'm not sure why but having rank set as int messes it up
enum Rank: Int { case Ace = 1 case Two, Three, Four, Five, Six, Seve, Eight, Nine, Ten case Jack, Queen, King func simpleDescription() -> String { switch self{ case .Ace: return "ace" case .Jack: return "jack" case .Queen: return "Queen" case .King: return "King" default: return String(self.toRaw()) } } mutating func next() -> Rank { var rank = self var rawrank = rank.toRaw() var nrank:Rank = self rawrank = rawrank + 1 if let newRank = Rank.fromRaw(rawrank) { println("\(newRank.simpleDescription())") nrank = newRank } else { return self } return nrank } } enum Suit { case Spades, Hearts, Diamonds, Clubs func color() -> String { switch self{ case .Spades, .Clubs: return "black" default: return "red" } } func simpleDescription() -> String { switch self{ case .Spades: return "spades" case .Hearts: return "hearts" case .Diamonds: return "diamonds" case .Clubs: return "clubs" } } mutating func next() -> Suit { switch self{ case .Spades: return Hearts case .Hearts: return Diamonds case .Diamonds: return Clubs case .Clubs: return Spades } } } struct Card { var rank:Rank var suit:Suit func deck() -> Card[] { var tRank = self.rank var tSuit = self.suit let tcards = 52 // we start from 0 var cards: Card[] = [] for i in 0..tcards{ var card = Card(rank: tRank, suit: tSuit) cards.append(card) tRank = tRank.next() tSuit = tSuit.next() } return cards } func simpleDescription() -> String { return "The \(rank.simpleDescription()) of \(suit.simpleDescription())" } } var card = Card(rank: .Ace, suit: .Spades) var deck = card.deck()
hope this helps basically I used a little general knowledge but that can be easily rectified by multiplying suits by rank (if you aren't using a standard deck of cards and you'd have to change the enums accordingly and if basically just steps through the different enums note to save time I used ranks raw values you could do the same for suits if you wanted however the example did not have it so I decided to figure it out without changing suits raw value
Sometimes, you may deal with an enumerated type with an underlying raw integer type that changes throughout the software development lifecycle. Here is an example that works well for that case:
public class MyClassThatLoadsTexturesEtc { //... // Colors used for gems and sectors. public enum Color: Int { // Colors arranged in order of the spectrum. case First = 0 case Red, Orange, Yellow, Green, Blue, Purple, Pink // --> Add more colors here, between the first and last markers. case Last } //... public func preloadGems() { // Preload all gems. for i in (Color.First.toRaw() + 1) ..< (Color.Last.toRaw()) { let color = Color.fromRaw(i)! loadColoredTextures(forKey: color) } } //... }