enum Suit: String {
case spades = "♠"
case hearts = "♥"
case diamonds = "♦"
case clubs = "♣"
}
Wie kann ich zum Beispiel so etwas tun:
for suit in Suit {
// do something with suit
print(suit.rawValue)
}
Ergebnisbeispiel:
♠
♥
♦
♣
Beginnen Sie mit Swift 4.2 (mit Xcode 10) und fügen Sie die Protokollkonformität zu CaseIterable
hinzu, um von allCases
zu profitieren:
extension Suit: CaseIterable {}
Dann werden alle möglichen Werte gedruckt:
Suit.allCases.forEach {
print($0.rawValue)
}
Imitieren Sie einfach die Implementierung von Swift 4.2:
#if !Swift(>=4.2)
public protocol CaseIterable {
associatedtype AllCases: Collection where AllCases.Element == Self
static var allCases: AllCases { get }
}
extension CaseIterable where Self: Hashable {
static var allCases: [Self] {
return [Self](AnySequence { () -> AnyIterator<Self> in
var raw = 0
var first: Self?
return AnyIterator {
let current = withUnsafeBytes(of: &raw) { $0.load(as: Self.self) }
if raw == 0 {
first = current
} else if current == first {
return nil
}
raw += 1
return current
}
})
}
}
#endif
Dieser Beitrag ist relevant hier https://www.Swift-studies.com/blog/2014/6/10/enumerating-enums-in-Swift
Im Wesentlichen ist die vorgeschlagene Lösung
enum ProductCategory : String {
case Washers = "washers", Dryers = "dryers", Toasters = "toasters"
static let allValues = [Washers, Dryers, Toasters]
}
for category in ProductCategory.allValues{
//Do something
}
Ich habe eine Hilfsfunktion iterateEnum()
für das Durchlaufen von Fällen für beliebige enum
-Typen erstellt.
Hier ist die Beispielnutzung:
enum Suit:String {
case Spades = "♠"
case Hearts = "♥"
case Diamonds = "♦"
case Clubs = "♣"
}
for f in iterateEnum(Suit) {
println(f.rawValue)
}
ausgänge:
♠
♥
♦
♣
Dies ist jedoch nur für Debugging oder TestZweck: Dies beruht auf mehreren undokumentierten aktuellen (Swift1.1) Compiler-Verhalten. Also auf eigene Gefahr benutzen :)
Hier ist der Code:
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
}
}
Die Grundidee ist:
enum
- ausgenommen enum
s mit zugehörigen Typen - ist nur ein Index von Fällen. Wenn die Anzahl der Fälle 2...256
ist, ist sie identisch mit UInt8
. Wenn 257...65536
, UInt16
und so weiter. Es kann also unsafeBitcast
von entsprechenden vorzeichenlosen Integer-Typen sein..hashValue
der Aufzählungswerte entspricht dem Index des Falls..hashValue
von Aufzählungswerten, die von ungültig übertragen wurden. Index ist 0
.HINZUGEFÜGT:
Für Swift2 überarbeitet und Casting-Ideen aus @ Kametrixoms Antwort umgesetzt
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: Überarbeitet für 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: Überarbeitet für 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
}
}
Die anderen Lösungen Arbeit aber sie alle setzen Annahmen über die Anzahl der möglichen Ränge und Anzüge oder den ersten und letzten Rang. Sicher, das Layout eines Kartensets wird sich in absehbarer Zeit wahrscheinlich nicht viel ändern. Im Allgemeinen ist es jedoch besser, Code zu schreiben, der so wenig Annahmen wie möglich macht. Meine Lösung:
Ich habe der Suit-Enumance einen Raw-Typ hinzugefügt, sodass ich Suit (rawValue :) verwenden kann, um auf die Suit-Fälle zuzugreifen:
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)
}
}
}
Unter der Implementierung der Methode createDeck () von Card. init (rawValue :) ist ein fehlgeschlagener Initialisierer und gibt einen optionalen Wert zurück. Durch das Auspacken und Überprüfen des Werts in beiden Anweisungen während der Anweisung müssen Sie nicht die Anzahl der Rang- oder Anzugfälle annehmen:
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
}
}
So rufen Sie die createDeck-Methode auf:
let card = Card(rank: Rank.Ace, suit: Suit.Clubs)
let deck = card.createDeck()
Also bin ich in den Bits und Bytes herumgestolpert und habe eine Erweiterung erstellt (die später herausfand, dass die Antwort von @rintaro sehr ähnlich ist). Es ist so verwendbar:
enum E : EnumCollection {
case A, B, C
}
Array(E.cases()) // [A, B, C]
Bemerkenswert ist, dass es für jedes Enum (ohne zugehörige Werte) verwendet werden kann. Beachten Sie, dass dies nicht für Aufzählungen funktioniert, die keine Fälle enthalten.
Wie bei der Antwort von @rintaro verwendet dieser Code die zugrunde liegende Darstellung einer Aufzählung. Diese Darstellung ist nicht dokumentiert und kann sich in der Zukunft ändern, was sie zerstören könnte -> Ich empfehle die Verwendung dieser Option in der Produktion nicht.
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
}
}
}
}
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
}
}
}
}
(Ich habe keine Ahnung, warum ich typealias
brauche, aber der Compiler beschwert sich ohne).
(Ich habe große Änderungen an dieser Antwort vorgenommen, siehe die Änderungen für frühere Versionen.)
Sie können eine Enumeration durchlaufen, indem Sie das ForwardIndexType
-Protokoll implementieren.
Das ForwardIndexType
-Protokoll erfordert, dass Sie eine successor()
-Funktion definieren, um die Elemente schrittweise durchzugehen.
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)!
}
Durch Iteration über einen offenen oder geschlossenen Bereich (..<
oder ...
) wird intern die Funktion successor()
aufgerufen, mit der Sie Folgendes schreiben können:
// 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
}
Im Prinzip ist es möglich, auf diese Weise zu tun, vorausgesetzt, dass Sie keine Rohwertzuordnung für Fälle von Enum verwenden:
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())")
}
Wenn Sie die Aufzählung einen rohen Int-Wert angeben, wird das Schleifen wesentlich einfacher.
Beispielsweise können Sie anyGenerator
verwenden, um einen Generator zu erhalten, der Ihre Werte auflisten kann:
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())
Dies scheint jedoch ein ziemlich allgemeines Muster zu sein. Wäre es nicht Nizza, wenn wir einen Enummentyp machen könnten, indem wir uns einfach an ein Protokoll halten? Gut mit Swift 2.0 und Protokollerweiterungen, jetzt können wir!
Fügen Sie dies einfach zu Ihrem Projekt hinzu:
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 }
}
Jedes Mal, wenn Sie eine Enumeration erstellen (solange es einen Int-Rohwert hat), können Sie es durch Protokollkonformität auflisten.
enum Rank: Int, EnumerableEnum {
case Ace, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King
}
// ...
for rank in Rank.enumerate() { ... }
Wenn Ihre Aufzählungswerte nicht mit 0
(Standardeinstellung) beginnen, überschreiben Sie die firstRawValue
-Methode:
enum DeckColor: Int, EnumerableEnum {
case Red = 10, Blue, Black
static func firstRawValue() -> Int { return Red.rawValue }
}
// ...
let colors = Array(DeckColor.enumerate())
Die letzte Suit-Klasse, einschließlich des Ersetzens von simpleDescription
durch das standardisiertes CustomStringConvertible-Protokoll , sieht folgendermaßen aus:
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)
}
EDIT:
Swift 3
-Syntax:
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
}
}
Auf Swift 2.2 + aktualisiert
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
}
}
}
der Code wurde in Swift 2.2-Formular @ Kametrixoms an swer aktualisiert
Für Swift 3.0+ (vielen Dank an @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
}
}
}
Dieses Problem ist jetzt viel einfacher. Hier ist meine Swift 4.2-Lösung.
enum Suit: Int, CaseIterable {
case None
case Spade, Heart, Diamond, Club
static let allNonNullCases = Suit.allCases[Spade.rawValue...]
}
enum Rank: Int, CaseIterable {
case Joker
case Two, Three, Four, Five, Six, Seven, Eight
case Nine, Ten, Jack, Queen, King, Ace
static let allNonNullCases = Rank.allCases[Two.rawValue...]
}
func makeDeck(withJoker: Bool = false) -> [Card] {
var deck = [Card]()
for suit in Suit.allNonNullCases {
for rank in Rank.allNonNullCases {
deck.append(Card(suit: suit, rank: rank))
}
}
if withJoker {
deck.append(Card(suit: .None, rank: .Joker))
}
return deck
}
Pre 4.2
Diese Lösung gefällt mir, nachdem ich diese Seite gefunden habe: Listenverständnis in Swift
Es verwendet Int-Raws anstelle von Strings, vermeidet jedoch zweimaliges Eingeben, ermöglicht das Anpassen der Bereiche und schreibt keine Rohwerte.
Dies ist die Swift 4-Version meiner ursprünglichen Lösung, siehe 4.2 oben.
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: Bool = false) -> [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
}
Ich habe festgestellt, dass ich in meinem Code .allValues
viel machte. Schließlich habe ich einen Weg gefunden, einfach einem Iteratable
-Protokoll zu entsprechen und eine rawValues()
-Methode zu haben.
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 = "h.e.b."
case Randalls = "randalls"
}
let groceryHashes = Grocery.hashValues() // AnyIterator<Grocery>
let groceryRawValues = Grocery.rawValues() // ["kroger", "h.e.b.", "randalls"]
BEARBEITEN: Vorschlag für eine schnelle EntwicklungSE-0194 Abgeleitete Sammlung von Enum-Fällen schlägt eine Ebene mit Lösung für dieses Problem vor. Wir sehen es in Swift 4.2 und neuer. Der Vorschlag weist auch auf einige Workarounds hin, die denen ähneln, die bereits hier erwähnt wurden, es könnte jedoch interessant sein, dies zu sehen.
Ich werde meinen ursprünglichen Posten auch der Vollständigkeit halber behalten.
Dies ist ein weiterer Ansatz, der auf der Antwort von @ Peymmankh basiert und an Swift 3 angepasst ist.
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)
}
}
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
}
}
Wie wäre es damit?
Entschuldigung, meine Antwort war spezifisch, wie ich diesen Beitrag in dem verwendet habe, was ich tun musste. Für diejenigen, die über diese Frage stolpern und nach einem Weg suchen, um einen Fall innerhalb einer Aufzählung zu finden , ist dies der Weg, dies zu tun (neu in Swift 2):
Bearbeiten: CamelCase in Kleinbuchstaben ist jetzt der Standard für Swift 3 Aufzählungswerte
// 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)
}
}
Für diejenigen, die sich über die Aufzählung in einer Aufzählung wundern, sind die Antworten auf dieser Seite, die eine statische Variable mit einem Array aller Aufzählungswerte enthalten, korrekt. Der neueste Apple Beispielcode für tvOS enthält genau dieselbe Technik.
Davon abgesehen sollten sie einen bequemeren Mechanismus in die Sprache einbauen (Apple, hören Sie zu?)!
In Swift 3 können Sie das {Strideable} -Protokoll implementieren, wenn das zugrunde liegende Enum {rawValue} hat. Die Vorteile sind, dass keine Werte-Arrays erstellt werden, wie in einigen anderen Vorschlägen, und dass die standardmäßige Swift-Anweisung "for i in ..." funktioniert, die für die Nice-Syntax sorgt.
// "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)")
}
Sie können versuchen, so aufzuzählen
enum Planet: String {
case Mercury
case Venus
case Earth
case Mars
static var enumerate: [Planet] {
var a: [Planet] = []
switch Planet.Mercury {
case .Mercury: a.append(.Mercury); fallthrough
case .Venus: a.append(.Venus); fallthrough
case .Earth: a.append(.Earth); fallthrough
case .Mars: a.append(.Mars)
}
return a
}
}
Planet.enumerate // [Mercury, Venus, Earth, Mars]
Damit bin ich am Ende gegangen; Ich denke, es ist das richtige Gleichgewicht zwischen Lesbarkeit und Wartbarkeit.
struct Card {
// ...
static func deck() -> Card[] {
var deck = Card[]()
for rank in Rank.Ace.toRaw()...Rank.King.toRaw() {
for suit in [Suit.Spades, .Hearts, .Clubs, .Diamonds] {
let card = Card(rank: Rank.fromRaw(rank)!, suit: suit)
deck.append(card)
}
}
return deck
}
let deck = Card.deck()
enum Filter: String, CaseIterable {
case salary = "Salary"
case experience = "Experience"
case technology = "Technology"
case unutilized = "Unutilized"
case unutilizedHV = "Unutilized High Value"
static let allValues = Filter.allCases.map { $0.rawValue }
}
Nennen
print(Filter.allValues)
Drucke:
["Gehalt", "Erfahrung", "Technologie", "ungenutzt", "ungenutzter hoher Wert"]
enum
, die Int
darstelltenum 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 }
}
Nenne es so:
print(Filter.allValues)
Drucke:
[0, 1, 2, 3, 4]
enum
, die String
darstelltenum 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"
}
}
}
Nennen
print(Filter.allValues)
Drucke:
["Gehalt", "Erfahrung", "Technologie", "ungenutzt", "ungenutzter hoher Wert"]
Das Experiment war: EXPERIMENT
Fügen Sie der Karte eine Methode hinzu, die ein volles Kartendeck mit einer Karte aus jeder Kombination von Rang und Farbe erstellt.
Also, ohne den angegebenen Code zu ändern oder zu verbessern, außer die Methode hinzuzufügen (und ohne etwas zu verwenden, das noch nicht gelehrt wurde), kam ich zu dieser Lösung:
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()
Beim Umgang mit Swift 2.0
ist hier mein Vorschlag:
Ich habe den Rohtyp zu Suit
enum
hinzugefügt.
enum Suit: Int {
dann:
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
}
}
Wie bei @Kametrixom answer here Ich glaube, ein Array zurückzugeben wäre besser als AnySequence, da man auf alle Goodies von Array wie count zugreifen kann.
Hier ist das Umschreiben:
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)
}
}
Ich habe es mit einer berechneten Eigenschaft gemacht, die das Array aller Werte zurückgibt (dank diesem Beitrag http://natecook.com/blog/2014/10/loopy-random-enum-ideas/ ). Es verwendet jedoch auch int raw-values, aber ich muss nicht alle Aufzählungsmitglieder in separaten Eigenschaften wiederholen.
UPDATE Xcode 6.1 hat ein bisschen geändert, wie man ein Enumerationsmitglied mit einem Rohwert erhält, also habe ich die Auflistung korrigiert. Kleiner Fehler mit falschem ersten Rohwert behoben
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++)
}
}
)
}
}
Enums haben die Methoden toRaw () und fromRaw (). Wenn Ihr Rohwert ein Int ist, können Sie von der ersten bis zur letzten Enumeration iterieren:
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()
}
}
Ein Grund dafür ist, dass Sie vor dem Ausführen der simpleDescription-Methode auf optionale Werte testen müssen. Daher setzen Sie convertSuit zuerst auf unseren Wert und dann eine Konstante auf convertSuit.simpleDescription ()
Dies ist ein ziemlich alter Beitrag aus Swift 2.0. Es gibt jetzt einige bessere Lösungen, die neuere Funktionen von Swift 3.0 verwenden: Iteration durch ein Enum in Swift 3.0
Und zu dieser Frage gibt es eine Lösung, die ein neues Feature von (das noch nicht veröffentlichte, während ich diesen Beitrag schreibe) Swift 4.2: verwendet.
Es gibt viele gute Lösungen in diesem Thread und andere, jedoch sind einige von ihnen sehr kompliziert. Ich mag es, so viel wie möglich zu vereinfachen. Hier ist eine Lösung, die möglicherweise für unterschiedliche Bedürfnisse funktioniert, aber ich denke, dass sie in den meisten Fällen gut funktioniert:
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
}
}
Zu iterieren:
for item in Number.allValues {
print("number is: \(item)")
}
Dies scheint ein Hack zu sein, aber wenn Sie rohe Werte verwenden, können Sie so etwas tun
enum Suit: Int {
case Spades = 0, Hearts, Diamonds, Clubs
...
}
var suitIndex = 0
while var suit = Suit.fromRaw(suitIndex++) {
...
}
Hier eine Methode, die ich verwende, um eine Aufzählung zu durchlaufen und mehrere Wertetypen aus einer Aufzählung bereitzustellen
enum IterateEnum: Int {
case Zero
case One
case Two
case Three
case Four
case Five
case Six
case Seven
//Tuple allows multiple values to be derived from the enum case, and
//since it is using a switch with no default, if a new case is added,
//a compiler error will be returned if it doesn't have a value Tuple set
var value: (french:String, spanish:String, japanese:String) {
switch self {
case .Zero: return (french:"zéro", spanish:"cero", japanese:"nuru")
case .One: return (french:"un", spanish:"uno", japanese:"ichi")
case .Two: return (french:"deux", spanish:"dos", japanese:"ni")
case .Three: return (french:"trois", spanish:"tres", japanese:"san")
case .Four: return (french:"quatre", spanish:"cuatro", japanese:"shi")
case .Five: return (french:"cinq", spanish:"cinco", japanese:"go")
case .Six: return (french:"six", spanish:"seis", japanese:"roku")
case .Seven: return (french:"sept", spanish:"siete", japanese:"shichi")
}
}
//Used to iterate enum or otherwise access enum case by index order.
//Iterate by looping until it returns nil
static func item(index:Int) -> IterateEnum? {
return IterateEnum.init(rawValue: index)
}
static func numberFromSpanish(number:String) -> IterateEnum? {
return findItem { $0.value.spanish == number }
}
//use block to test value property to retrieve the enum case
static func findItem(predicate:((_:IterateEnum)->Bool)) -> IterateEnum? {
var enumIndex:Int = -1
var enumCase:IterateEnum?
//Iterate until item returns nil
repeat {
enumIndex += 1
enumCase = IterateEnum.item(index: enumIndex)
if let eCase = enumCase {
if predicate(eCase) {
return eCase
}
}
} while enumCase != nil
return nil
}
}
var enumIndex:Int = -1
var enumCase:IterateEnum?
//Iterate until item returns nil
repeat {
enumIndex += 1
enumCase = IterateEnum.item(index: enumIndex)
if let eCase = enumCase {
print("The number \(eCase) in french: \(eCase.value.french), spanish: \(eCase.value.spanish), japanese: \(eCase.value.japanese)")
}
} while enumCase != nil
print("Total of \(enumIndex) cases")
let number = IterateEnum.numberFromSpanish(number: "siete")
print("siete in japanese: \((number?.value.japanese ?? "Unknown"))")
Dies ist die Ausgabe:
Die Nummer Null in Französisch: Zéro, Spanisch: Cero, Japanisch: Nuru
Die Nummer Eins in Französisch: un, Spanisch: Uno, Japanisch: Ichi
Die Nummer Zwei in Französisch: Deux, Spanisch: Dos, Japanisch: Ni
Die Nummer Drei in Französisch: Trois, Spanisch: Tres, Japanisch: San
Die Nummer Vier auf Französisch: Quatre, Spanisch: Cuatro, Japanisch: Shi
Die Nummer Fünf in Französisch: Cinq, Spanisch: Cinco, Japanisch: Los
Die Nummer Sechs in Französisch: Sechs, Spanisch: Seis, Japanisch: Roku
Die Nummer Sieben in Französisch: Sept, Spanisch: Siete, Japanisch: Shichi
Insgesamt 8 Fälle
siete auf japanisch: shichi
UPDATE
Ich habe vor kurzem ein Protokoll für die Aufzählung erstellt. Das Protokoll erfordert eine Aufzählung mit einem Int-Rohwert:
protocol EnumIteration {
//Used to iterate enum or otherwise access enum case by index order. Iterate by looping until it returns nil
static func item(index:Int) -> Self?
static func iterate(item:((index:Int, enumCase:Self)->()), completion:(()->())?) {
static func findItem(predicate:((enumCase:Self)->Bool)) -> Self?
static func count() -> Int
}
extension EnumIteration where Self: RawRepresentable, Self.RawValue == Int {
//Used to iterate enum or otherwise access enum case by index order. Iterate by looping until it returns nil
static func item(index:Int) -> Self? {
return Self.init(rawValue: index)
}
static func iterate(item:((index:Int, enumCase:Self)->()), completion:(()->())?) {
var enumIndex:Int = -1
var enumCase:Self?
//Iterate until item returns nil
repeat {
enumIndex += 1
enumCase = Self.item(enumIndex)
if let eCase = enumCase {
item(index: enumIndex, enumCase: eCase)
}
} while enumCase != nil
completion?()
}
static func findItem(predicate:((enumCase:Self)->Bool)) -> Self? {
var enumIndex:Int = -1
var enumCase:Self?
//Iterate until item returns nil
repeat {
enumIndex += 1
enumCase = Self.item(enumIndex)
if let eCase = enumCase {
if predicate(enumCase:eCase) {
return eCase
}
}
} while enumCase != nil
return nil
}
static func count() -> Int {
var enumIndex:Int = -1
var enumCase:Self?
//Iterate until item returns nil
repeat {
enumIndex += 1
enumCase = Self.item(enumIndex)
} while enumCase != nil
//last enumIndex (when enumCase == nil) is equal to the enum count
return enumIndex
}
}
Hier ist mein vorgeschlagener Ansatz. Es ist nicht völlig zufriedenstellend (ich bin sehr neu in Swift und OOP!), Aber vielleicht kann jemand es verfeinern. Die Idee ist, dass jedes Enum seine eigenen Bereichsinformationen als erste und letzte Eigenschaft bereitstellt. Es fügt jeder Aufzählung nur zwei Zeilen Code hinzu: immer noch ein wenig hartcodiert, aber zumindest wird nicht der gesamte Satz dupliziert. Es ist nicht erforderlich, die Suit-Enumeration so zu ändern, dass sie ein Int ist, wie es bei der Rank-Enumeration der Fall ist.
Anstatt die gesamte Lösung zu wiederholen, ist hier der Code, den ich zur Rank-Aufzählung hinzugefügt habe, irgendwo nach den Case-Anweisungen (Suit-Enum ist ähnlich):
var first: Int { return Ace.toRaw() }
var last: Int { return King.toRaw() }
und die Schleife, mit der ich das Deck als String-Array erstellt habe. (In der Problemdefinition wurde nicht angegeben, wie das Deck strukturiert werden soll.)
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
}
Dies ist unbefriedigend, da die Eigenschaften einem Element und nicht der Aufzählung zugeordnet sind. Die 'for'-Schleifen werden jedoch klarer. Ich möchte, dass Rank.first anstelle von Rank.Ace.first gesagt wird. Es funktioniert (mit jedem Element), aber es ist hässlich. Kann jemand zeigen, wie man das auf das Enumenniveau erhöht?
Damit das funktioniert, habe ich die createDeck-Methode aus der Card-Struktur genommen. Ich konnte nicht herausfinden, wie ein [String] -Array von dieser Struktur zurückgegeben wird, und das scheint ein schlechter Ort für eine solche Methode zu sein.
Eine andere Lösung:
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)
}
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
}
}
Basierend auf Rick Antwort: Das ist 5 mal schneller
(Verbesserung der Antwort von Karthik Kumar)
Diese Lösung verwendet den Compiler, um sicherzustellen, dass Sie keinen Fall verpassen.
enum Suit: String {
case spades = "♠"
case hearts = "♥"
case diamonds = "♦"
case clubs = "♣"
static var enumerate: [Suit] {
switch Suit.spades {
// make sure the two lines are identical ^_^
case .spades, .hearts, .diamonds, .clubs:
return [.spades, .hearts, .diamonds, .clubs]
}
}
}
Es gibt einen klugen Weg, und es ist frustrierend, da es den Unterschied zwischen den zwei verschiedenen Arten von Enummen veranschaulicht.
Versuche dies:
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
}
Der Deal ist, dass eine Enumeration, die durch Zahlen (Rohwerte) unterstützt wird, implizit explizit geordnet ist, während eine Enumeration, die nicht durch Zahlen unterlegt ist, explizit implizit ungeordnet ist.
Z.B. Wenn wir die Aufzählungswerte angeben, ist die Sprache gerissen genug, um herauszufinden, in welcher Reihenfolge die Zahlen sind .. Wenn wir dagegen keine Reihenfolge angeben, versuchen wir, die Sprache durch die Werte zu iterieren wirft seine Hände in die Luft und geht "ja, aber welche willst du zuerst machen ???"
Andere Sprachen, die dies tun können (Iterieren über ungeordnete Enumerationen), können dieselben Sprachen sein, in denen sich alles unter der Haube befindet, eigentlich eine Karte oder ein Wörterbuch, und Sie können über die Schlüssel einer Map iterieren, ob es eine logische Reihenfolge gibt oder nicht.
Der Trick besteht also darin, etwas explizit zu ordnen, in diesem Fall Fälle der Farben eines Arrays in der gewünschten Reihenfolge. Sobald Sie es geben, ist Swift wie "Nun, warum haben Sie das überhaupt nicht gesagt?"
Der andere Kurztrick besteht darin, den Forcen-Operator für die fromRaw-Funktion zu verwenden. Dies verdeutlicht ein weiteres "Gotcha" über Enumerationen, dass der Bereich der möglichen Werte, die übergeben werden können, oft größer ist als der Enumerationsbereich. Wenn wir beispielsweise Rank.fromRaw (60) sagen, wird kein Wert zurückgegeben. Daher verwenden wir die Option optional der Sprache. Wo wir mit der Verwendung von Optionals beginnen, wird das Forcen bald folgen. (Oder alternativ die if let -Konstruktion, die mir immer noch ein bisschen komisch erscheint)
Swift 5 Lösung: Die Lösung in Swift 5 ist ganz einfach:
enum Suit: String, CaseIterable {
case spades = "♠"
case hearts = "♥"
case diamonds = "♦"
case clubs = "♣"
}
// then access the cases like this:
for suitKey in LocalizationKey.allCases {
print(suitKey)
}
Ich habe die Methode unten verwendet, die Annahme ist, dass ich weiß, welcher der letzte Wert in der Rank-Aufzählung ist und dass alle Ränge inkrementelle Werte nach Ace haben
Ich bevorzuge diesen Weg, da er sauber und klein ist und leicht verständlich ist
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
}
In einigen Fällen handelt es sich möglicherweise um einen aufgezählten Typ mit einem zugrunde liegenden rohen Integer-Typ, der sich während des gesamten Lebenszyklus der Softwareentwicklung ändert. Hier ist ein Beispiel, das für diesen Fall gut funktioniert:
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)
}
}
//...
}
Ich brauchte ein wenig mehr als nur eine Methode in der Struktur, wie das von Swift geforderte Buch, aber ich habe die nächsten Funktionen in der Enumeration eingerichtet. Ich hätte ein Protokoll verwendet. Ich bin mir nicht sicher, warum es so ist
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()
ich hoffe, das hilft im Grunde ein wenig allgemeines Wissen, aber das lässt sich leicht durch Multiplizieren von Anzügen nach Rang korrigieren (wenn Sie kein Standard-Kartendeck verwenden und Sie die Enummen entsprechend ändern müssen und im Grunde nur durchlaufen müssen.) Um die Zeit zu sparen, habe ich verschiedene Werte verwendet. Ich habe Ränge verwendet, die Sie für Anzüge verwenden könnten
Ich habe die Funktion count () hinzugefügt und die Werte iteriert:
public enum MetricType : Int {
case mvps = 0
case allNBA = 1
case championshipRings = 2
case finalAppearances = 3
case gamesPlayed = 4
case ppg = 5
static func count() -> Int {
return (ppg.rawValue) + 1
}
static var allValues: [MetricType] {
var array: [MetricType] = Array()
var item : MetricType = MetricType.mvps
while item.rawValue < MetricType.count() {
array.append(item)
item = MetricType(rawValue: (item.rawValue + 1))!
}
return array
}
}
Meine Lösung ist, ein Array mit allen Aufzählungsmöglichkeiten zu deklarieren, damit alle durchlaufen werden können.
//Function inside struct Card
static func generateFullDeck() -> [Card] {
let allRanks = [Rank.Ace, Rank.Two, Rank.Three, Rank.Four, Rank.Five, Rank.Six, Rank.Seven, Rank.Eight, Rank.Nine, Rank.Ten, Rank.Jack, Rank.Queen, Rank.King]
let allSuits = [Suit.Hearts, Suit.Diamonds, Suit.Clubs, Suit.Spades]
var myFullDeck: [Card] = []
for myRank in allRanks {
for mySuit in allSuits {
myFullDeck.append(Card(rank: myRank, suit: mySuit))
}
}
return myFullDeck
}
//actual use:
let aFullDeck = Card.generateFullDeck() //Generate the desired full deck
var allDesc: [String] = []
for aCard in aFullDeck {
println(aCard.simpleDescription()) //You'll see all the results in playground
}
Ich fand ein etwas hässliches Gefühl, aber viel sicherer, dies zu tun, ohne die Werte zweimal einzugeben oder auf das Gedächtnis der enum-Werte zu verweisen.
Erstellen Sie statt einer Aufzählung eine Struktur mit einer einzelnen Instanz und alle Konstanten der Aufzählungswerte. Die Variablen können dann mit einer Mirror
abgefragt werden.
public struct Suit{
// the values
let spades = "♠"
let hearts = "♥"
let diamonds = "♦"
let clubs = "♣"
// make a single instance of the Suit struct, Suit.instance
struct SStruct{static var instance: Suit = Suit()}
static var instance : Suit{
get{return SStruct.instance}
set{SStruct.instance = newValue}
}
// an array with all of the raw values
static var allValues: [String]{
var values = [String]()
let mirror = Mirror(reflecting: Suit.instance)
for (_, v) in mirror.children{
guard let suit = v as? String else{continue}
values.append(suit)
}
return values
}
}
Wenn Sie diese Methode verwenden, müssen Sie zum Abrufen eines einzelnen Werts Suit.instance.clubs
oder Suit.instance.spades
verwenden.
Aber das alles ist so langweilig.
public struct SuitType{
// store multiple things for each suit
let spades = Suit("♠", order: 4)
let hearts = Suit("♥", order: 3)
let diamonds = Suit("♦", order: 2)
let clubs = Suit("♣", order: 1)
struct SStruct{static var instance: SuitType = SuitType()}
static var instance : SuitType{
get{return SStruct.instance}
set{SStruct.instance = newValue}
}
// a dictionary mapping the raw values to the values
static var allValuesDictionary: [String : Suit]{
var values = [String : Suit]()
let mirror = Mirror(reflecting: SuitType.instance)
for (_, v) in mirror.children{
guard let suit = v as? Suit else{continue}
values[suit.rawValue] = suit
}
return values
}
}
public struct Suit: RawRepresentable, Hashable{
public var rawValue: String
public typealias RawValue = String
public var hashValue: Int{
// find some integer that can be used to uniquely identify
// each value. In this case, we could have used the order
// variable because it is a unique value, yet to make this
// apply to more cases, the hash table address of rawValue
// will be returned, which should work in almost all cases
//
// you could also add a hashValue parameter to init() and
// give each suit a different hash value
return rawValue.hash
}
public var order: Int
public init(_ value: String, order: Int){
self.rawValue = value
self.order = order
}
// an array of all of the Suit values
static var allValues: [Suit]{
var values = [Suit]()
let mirror = Mirror(reflecting: SuitType.instance)
for (_, v) in mirror.children{
guard let suit = v as? Suit else{continue}
values.append(suit)
}
return values
}
// allows for using Suit(rawValue: "♦"), like a normal enum
public init?(rawValue: String){
// get the Suit from allValuesDictionary in SuitType, or return nil if that raw value doesn't exist
guard let suit = SuitType.allValuesDictionary[rawValue] else{return nil}
// initialize a new Suit with the same properties as that with the same raw value
self.init(suit.rawValue, order: suit.order)
}
}
Du kannst jetzt Sachen machen
let allSuits: [Suit] = Suit.allValues
oder
for suit in Suit.allValues{
print("The suit \(suit.rawValue) has the order \(suit.order)")
}
Um eine Single zu erhalten, müssen Sie jedoch SuitType.instance.spades
oder SuitType.instance.hearts
verwenden. Um dies ein wenig intuitiver zu gestalten, können Sie Suit
etwas Code hinzufügen, mit dem Sie Suit.type.*
anstelle von SuitType.instance.*
verwenden können.
public struct Suit: RawRepresentable, Hashable{
// ...your code...
static var type = SuitType.instance
// ...more of your code...
}
Sie können jetzt Suit.type.diamonds
anstelle von SuitType.instance.diamonds
oder Suit.type.clubs
anstelle von SuitType.instance.clubs
verwenden.
Anpassung von @ rintaros Antwort an Swift 3, in dem rintaro:
iterateEnum()
für das Durchlaufen von Fällen für beliebige AufzählungstypenSwift3
func iterateEnum<T: Hashable>(_: T.Type) -> AnyGenerator<T> {
var i = 0
return AnyGenerator {
let next = withUnsafePointer(&i) { UnsafePointer<T>($0).memory }
let value : T?
if next.hashValue == i {
value = next
} else {
value = nil
}
i = i + 1
return value
}
}