Come enumerare un enum con tipo String?


530
enum Suit: String {
    case spades = "♠"
    case hearts = "♥"
    case diamonds = "♦"
    case clubs = "♣"
}

Ad esempio, come posso fare qualcosa del tipo:

for suit in Suit {
    // do something with suit
    print(suit.rawValue)
}

Esempio risultante:



In quale caso non conosceresti il ​​tipo?
mtbennett,

Hai ragione, in questo caso è di tipo String.
Lucien,

Ancora nessuna riflessione su Swift ...
Sulthan,

22
Non è ironico che vengano chiamate enumerazioni, ma sono così dolorosamente fastidiose da elencare in Swift?
Charlton Provatas,

3
@CharltonProvatas Se quello fosse l'unico inconveniente di Swift, lo chiamerei un giorno. Guardando quante persone offrono diverse soluzioni alternative per questo, sto solo rosicchiando la mia tastiera.
qwerty_so,

Risposte:


267

Swift 4.2+

A partire da Swift 4.2 (con Xcode 10), è sufficiente aggiungere la conformità del protocollo CaseIterableper trarne vantaggio allCases. Per aggiungere questa conformità del protocollo, devi semplicemente scrivere da qualche parte:

extension Suit: CaseIterable {}

Se l'enum è tuo, puoi specificare la conformità direttamente nella dichiarazione:

enum Suit: String, CaseIterable { case spades = "♠"; case hearts = "♥"; case diamonds = "♦"; case clubs = "♣" }

Quindi il seguente codice stamperà tutti i possibili valori:

Suit.allCases.forEach {
    print($0.rawValue)
}

Compatibilità con le versioni precedenti di Swift (3.xe 4.x)

Se devi supportare Swift 3.xo 4.0, puoi imitare l'implementazione di Swift 4.2 aggiungendo il seguente codice:

#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

@DmitryPetukhov Sarei felice di aiutarti, ma: (1) sei sicuro di avere l'ultima versione del codice? (alcuni crash sono stati risolti un mese fa) e (2) si prega di fornire un MCVE del proprio tipo personalizzato in grado di riprodurre un crash e la propria versione di Xcode.
Cœur

Questo funziona bene per me per le build di debug, ma non appena creo una versione e carica su TestFlight si blocca. Apple in qualche modo lo sta togliendo?
Daniel Wood,

1
Sembra che la tua versione abbia un aspetto positivo rispetto alla versione integrata di CaseIterator. Posso estendere gli enum che sono definiti in un altro file con la tua versione. Se rendi pubblici tutti i casi nell'estensione, puoi anche estendere gli enum definiti in diversi framework.
adamfowlerphoto,

1
@CyberMew Ho aggiornato la risposta per chiarirla. Il libro Swift e le note di rilascio di Xcode 10 di CaseIterable sono posteriori alla mia risposta e hanno usato esempi semplificati in cui l'enum non è supportato String, al contrario della presente domanda Stack Overflow.
Cor

1
Vorrei sottolineare l'importanza di "# if! Swift (> = 4.2)". Se hai scritto il tuo codice prima di swift 4.2 e hai dimenticato di rimuovere il codice "# if! Swift (> = 4.2)", quando usi Xcode versione 11.4 per compilare localmente sul tuo dispositivo di test, tutto andrà bene. Ma quando l'app viene scaricata dall'app store o da un volo di prova, quel pezzo di codice si bloccherà. Questo tipo di bug è molto difficile da individuare o eseguire il debug.
Oliver Zhang,

524

Questo post è pertinente qui https://www.swift-studies.com/blog/2014/6/10/enumerating-enums-in-swift

Essenzialmente la soluzione proposta è

enum ProductCategory : String {
     case Washers = "washers", Dryers = "dryers", Toasters = "toasters"

     static let allValues = [Washers, Dryers, Toasters]
}

for category in ProductCategory.allValues{
     //Do something
}

188
Bello, ma ... devi inserire i tuoi elementi dell'enumerazione due volte - una volta per l'enumerazione, una volta per tutti i valori. Non esattamente elegante come uno vorrebbe.
Jay Imerman,

2
Concordo con il "ma" ... comunque, come indicato nell'articolo, forse c'è un problema che un'enum è davvero un set e quindi non ordinata ... intendiamoci ... i casi di ordine definiti in non sarebbero un brutto inizio!
rougeExciter il

3
In Java il compilatore fa questo per te, forse anche Swift 2.0 lo farà. In particolare in Java tutti gli enum ottengono una descrizione (toString in Java) che fornisce alla stringa i nomi dei casi (Rondelle, ...) e viene automaticamente creato un insieme di casi. Java offre anche l'indicizzazione della posizione. Come ho già detto, forse Swift 2.0.
Howard Lovatt,

1
Idealmente, avresti qualcosa di simile all'implementazione di c # in cui puoi fare Enum.Values(typeof(FooEnum))ma esposto come metodo di estensione (come mappare o ridurre). FooEnum.values() :: values(EnumType -> [EnumType])
Rodrigoelp,

1
L'articolo fa un buon punto alla fine sul fatto che ogni valore enum sia nell'array allValues ​​con un test unitario. Tuttavia, riesco ancora a vedere qualcuno che aggiunge altri elementi, ma non li impone nel test unitario che ci lascia ancora indietro all'inizio, non sapendo con certezza che tutti i valori di enum sono mantenuti in tutti i valori.
DonnaLea,

278

Ho creato una funzione di utilità iterateEnum()per iterare casi per enumtipi arbitrari .

Ecco un esempio di utilizzo:

enum Suit: String {
    case Spades = "♠"
    case Hearts = "♥"
    case Diamonds = "♦"
    case Clubs = "♣"
}

for f in iterateEnum(Suit) {
    println(f.rawValue)
}

Quali uscite:

♠
♥
♦
♣

Ma questo è solo a scopo di debug o di test : si basa su diversi comportamenti del compilatore Swift1.1 non documentati, quindi utilizzalo a tuo rischio.

Ecco il codice:

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

L'idea alla base è:

  • La rappresentazione della memoria di enum, esclusi i enums con i tipi associati, è solo un indice di casi in cui il conteggio dei casi è 2...256identico a UInt8, quando 257...65536, è UInt16e così via. Quindi, può provenire unsafeBitcastdai corrispondenti tipi interi senza segno.
  • .hashValue dei valori enum è lo stesso dell'indice del caso.
  • .hashValuedei valori enum bitcastati da un indice non valido è 0.

Revisionato per Swift2 e implementato idee di casting dalla risposta di @ 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
    }
}

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

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

20
Fantastica e l'unica risposta che risponde alla domanda! Ma sì ... non lo toccherò! +1 per sforzo, però!
dotToString

Ho appena pubblicato la mia risposta che funziona sostanzialmente allo stesso modo (ho visto questa risposta solo in seguito). Utilizza Swift 2.0 beta 6 e le moderne funzionalità del linguaggio.
Kametrixom,

2
La versione Swift 3 funziona bene. Ho solo bisogno di modificare un po 'l'uso: for f in iterateEnum (Suit.self) {print (f.rawValue)}
Gene De Lisa,

16
+1 questo è abbastanza brillante. È anche, IMHO, troppo intelligente da usare, come dimostra la sua rottura significativa in ogni importante cambio di versione di Swift. A merito dell'autore, la versione di Swift 3 è stata realizzata un mese prima che Swift 3 uscisse dalla beta ... Se hai intenzione di prendere questa risposta e imparare tutto questo withUnsafePointer withMemoryRebounde pointeeroba del genere, allora usala in ogni modo. Altrimenti, lo eviterei.
Dan Rosenstark,

5
Voglio solo aggiungere che ora è rotto in Swift 4, ma solo su Linux, quindi +1 ai commenti sopra che è troppo intelligente da usare.
Andrew Plummer,

130

Le altre soluzioni funzionano ma fanno tutte ipotesi, ad esempio, sul numero di gradi e semi possibili o su quale possa essere il primo e l'ultimo grado. È vero, il layout di un mazzo di carte probabilmente non cambierà molto nel prossimo futuro. In generale, tuttavia, è meglio scrivere codice che faccia il minor numero possibile di ipotesi. La mia soluzione:

Ho aggiunto un tipo non Suitelaborato all'enum, quindi posso usare Suit(rawValue:)per accedere ai Suitcasi:

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

Di seguito l'implementazione del createDeck()metodo Card . init(rawValue:)è un inizializzatore disponibile e restituisce un facoltativo. Con scartare e controllando il suo valore in entrambe le istruzioni, mentre, non c'è bisogno di assumere il numero di Ranko di Suitcasi:

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

Ecco come chiamare il createDeckmetodo:

let card = Card(rank: Rank.Ace, suit: Suit.Clubs)
let deck = card.createDeck()

12
Risposta MIGLIORE assoluta che ho visto su vari thread su questo argomento. Molto elegante. Funziona con le enumerazioni del tipo Int, ma mi chiedo come si possa iterare attraverso altri tipi (stringhe, tipi personalizzati, ecc.).
Jay Imerman,

3
Questa è sicuramente la soluzione migliore. Una cosa da notare. Nell'esempio del libro non ha "case Spades = 1" come sdduursma. All'inizio non l'ho preso. questa è un'opzione, oppure puoi semplicemente usare "var m = 0"
Joshua Goossen,

10
Questo presuppone che i valori grezzi siano sequenziali. Quando ciò non è vero, ad esempio quando l'enum rappresenta i flag di maschera di bit, il loop si chiude prematuramente.
Jim,

1
Questa soluzione presuppone che sia possibile modificare la definizione di Suit. In questo esempio puoi, ma l'esercizio ha lo scopo di farti lavorare con ciò che enumsti è stato dato come se provenissero da una fonte esterna.
Josh J,

1
L'unica cosa di cui mi lamento è che non posso chiamarlo come metodo statico, ma prima devo creare un oggetto card.
Qed

76

Mi sono imbattuto in bit e byte e ho creato un'estensione che in seguito ho scoperto funziona molto simile alla risposta di @rintaro . È usato così:

enum E : EnumCollection {
    case A, B, C
}

Array(E.cases())    // [A, B, C]

La cosa straordinaria è che è utilizzabile su qualsiasi enum senza valori associati. Nota che questo non funziona per gli enum che non hanno casi.

Come nella risposta di @rintaro , questo codice usa la rappresentazione sottostante di un enum. Questa rappresentazione non è documentata e potrebbe cambiare in futuro, il che la spezzerebbe. Non consiglio l'uso di questo in produzione.

Codice (Swift 2.2, Xcode 7.3.1, non funzionante su Xcode 10):

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

Codice (Swift 3, Xcode 8.1, non funzionante su Xcode 10):

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

Non ho idea del perché mi serva typealias, ma il compilatore si lamenta senza.


10
Questa risposta è persino migliore della mia risposta, specialmente nella parte casting :)
rintaro,

Ma penso che questo funzioni solo sul piccolo ambiente endian?
Rintaro,

5
Xcode 8 beta 6 ha cambiato di nuovo questo! Viene visualizzato il seguente errore `` init 'non è disponibile: utilizzare' withMemoryRebound (to: capacity: _) 'per visualizzare temporaneamente la memoria come un altro tipo compatibile con il layout.`
Vorlon confuso,

1
@ConfusedVorlon: vedi la risposta sopra di @Rintaro: sostituisci withUnsafePointer... pointee}diwithUnsafePointer(to: &i) { $0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee } }
Stefan

1
Questo sembra non funzionare più a partire da Xcode 10. (So che non sarebbe necessario con Swift 4.2) ma quando si utilizza Swift 4 (.1) in Xcode 10 questo codice non funziona più (il valore grezzo è disuguale)
benrudhart

26

È possibile iterare attraverso un enum implementando il ForwardIndexTypeprotocollo.

Il ForwardIndexTypeprotocollo richiede di definire una successor()funzione per scorrere gli elementi.

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

L'iterazione su un intervallo aperto o chiuso ( ..<o ...) chiamerà internamente la successor()funzione che consente di scrivere questo:

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

2
Trovo che questa sia la risposta più "corretta" alla domanda, anche la più "elegante" (codice extra necessario di fronte ad altre opzioni qui presenti) data la sintassi risultante quando usata all'interno di un intervallo (la sintassi è ciò che si aspetterebbe di essere in grado di fare se enum in cui enumerable sans soluzioni alternative). Grazie! Anche se vale la pena notare che se il sovraccarico dell'operatore viene impiegato altrove oltre al successore () (che sembra allettante), ovviamente lo scartamento forzato è pericoloso. Inoltre, l'informazione sembra non necessaria ...?
iOS Gamer,

Risposta aggiornata per riflettere le ultime specifiche della lingua Swift
RndmTsk

Un successor()metodo correttamente definito (prima opzione) eliminerebbe la necessità enumdi avere un tipo associato. +1
nhgrif

1
Ma questa elegante risposta non funzionerà con gli enumeratori di stringhe, vero?
Ali,

La soluzione più "corretta" / best practice! + 1-ed
Siu Ching Pong -Asuka Kenji-

18

Questo problema è ora molto più semplice. Ecco la mia soluzione Swift 4.2:

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:

Mi piace questa soluzione che ho messo insieme dopo aver trovato " Comprensione della lista in Swift ".

Utilizza i raw Int invece delle stringhe ma evita di digitare due volte, consente di personalizzare gli intervalli e non codifica i valori grezzi.

Questa è una versione Swift 4 della mia soluzione originale, ma vedi il miglioramento 4.2 sopra:

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
}

Oh, vedo ora che il mio è fondamentalmente lo stesso di Sutean Rutjanalard.
adazacom,

1
In realtà, mi è piaciuta molto la tua implementazione. Penso che sia più chiaro! 1 voto. In realtà le risposte più votate sono troppo intelligenti e sicuramente si romperanno in futuro. Il tuo promette un po 'di stabilità in futuro.
jvarela,

17

In linea di principio è possibile farlo in questo modo assumendo che non si usi l'assegnazione di valori grezzi per i casi di enum:

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

7
Questo è carino ma funziona solo per gli enumerativi Integer continui a partire da 0
Robert

@Robert, come afferma il mio commento sopra: "non usi l'assegnazione di valori grezzi per i casi di enum"
Alfa07

Sì, non utilizzare valori non elaborati e impostare il tipo sottostante su int. In breve tempo non hai bisogno di un tipo per un enum come nell'esempio dei semi. enum ItWontWorkForThisEnum {case a, b, c}
Robert,

Come risolverebbe questo problema se una tupla fosse associata al caso di enumerazione?
rougeExciter l'

Non puoi associare una tupla a un enum molto facilmente.
nhgrif,

13

Se dai all'enum un valore Int grezzo, questo renderà il loop molto più semplice.

Ad esempio, puoi usare anyGeneratorper ottenere un generatore in grado di enumerare i tuoi valori:

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

Tuttavia, questo sembra un modello abbastanza comune, non sarebbe bello se potessimo rendere enumerabile qualsiasi tipo di enum semplicemente conformandoci a un protocollo? Bene con Swift 2.0 e le estensioni di protocollo, ora possiamo!

Aggiungilo semplicemente al tuo progetto:

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

Ora, ogni volta che crei un enum (purché abbia un valore raw Int), puoi renderlo enumerabile conformandoti al protocollo:

enum Rank: Int, EnumerableEnum {
    case Ace, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King
}
// ...
for rank in Rank.enumerate() { ... }

Se i valori di enum non iniziano con 0(impostazione predefinita), sovrascrivi il firstRawValuemetodo:

enum DeckColor: Int, EnumerableEnum {
    case Red = 10, Blue, Black
    static func firstRawValue() -> Int { return Red.rawValue }
}
// ...
let colors = Array(DeckColor.enumerate())

La classe Suit finale, inclusa la sostituzione simpleDescriptioncon il protocollo CustomStringConvertible più standard , sarà simile a questa:

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

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

nextIndex ++ verrà rimosso in swift 3. Cosa suggerisci in sostituzione di - var nextIndex = firstRawValue () return anyGenerator {Self (rawValue: nextIndex ++)}
Avi

Capito. defer {nextIndex + = 1} return AnyGenerator {Self (rawValue: nextIndex)}
Avi,

12

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

È il codice aggiornato a Swift 2.2 dalla risposta di Kametrixom

Per Swift 3.0+ (molte grazie a @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
        }
    }
}

@silvansky potresti per favore spiegare cosa intendi?
ale_stro,

Oops, scusa, ho ricontrollato e c'era un bug nel parco giochi: nel progetto reale questo codice funziona come previsto, grazie! =)
silvansky,

1
Ottima soluzione! Inoltre, funziona bene su Swift 3 con modifiche minime ("AnyGenerator" rinominato "AnyIterator" e ".memory" rinominato in ".pointee").
Filippo,

9

Soluzione Swift 5:

enum Suit: String, CaseIterable {
    case spades = "♠"
    case hearts = "♥"
    case diamonds = "♦"
    case clubs = "♣"
}

// access cases like this:

for suitKey in Suit.allCases {
    print(suitKey)
}

7

Mi sono ritrovato a fare .allValuesmolto durante il mio codice. Alla fine ho scoperto un modo per conformarmi semplicemente a un Iteratableprotocollo e avere un rawValues()metodo.

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"]

7

Xcode 10 con Swift 4.2

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

Chiamalo

print(Filter.allValues)

stampe:

["Stipendio", "Esperienza", "Tecnologia", "Non utilizzato", "Alto valore non utilizzato"]


Versioni precedenti

Per enumrappresentareInt

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

Chiamalo così:

print(Filter.allValues)

stampe:

[0, 1, 2, 3, 4]


Per enumrappresentareString

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

Chiamalo

print(Filter.allValues)

stampe:

["Stipendio", "Esperienza", "Tecnologia", "Non utilizzato", "Alto valore non utilizzato"]


7

EDIT: Swift Evolution Proposta SE-0194 La raccolta derivata di casi Enum propone una soluzione mirata a questo problema. Lo vediamo in Swift 4.2 e versioni successive. La proposta evidenzia anche alcune soluzioni alternative simili ad alcune già menzionate qui, ma potrebbe essere interessante vedere comunque.

Terrò anche il mio post originale per completezza.


Questo è un altro approccio basato sulla risposta di @ Peymmankh, adattato a 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)
}

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

Cosa ne pensi di questo?


Grazie, funziona come ho bisogno. Ma c'è qualche opportunità nella chiusura della mappa per ottenere il valore non per indice ma per nome?
Mikhail,

4

Puoi provare a enumerare in questo modo

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]

1
È un sacco di codice inutile! È equivalente a static var enumerate = [Mercury, Venus, Earth, Mars], rendendolo una risposta scadente rispetto alla risposta più votata stackoverflow.com/a/24137319/1033581
Cœur

@ Cœur questa risposta ha l'importante vantaggio di utilizzare il compilatore per garantire che non si perda un caso.
dchakarov,

@Cœur che ha lo stesso problema nel permetterti di fare un errore utente, cioè il compilatore non si lamenterà se scrivi return [Mercury, Venus, Mars]invece direturn [Mercury, Venus, Earth, Mars]
dchakarov

@dchakarov ho deciso di postare il miglioramento come una risposta, per chiarezza: stackoverflow.com/a/50409525/1033581
Cœur

@ Cœur Se nella tua nuova risposta sostituisci la dichiarazione di ritorno con questa, return [.spades, .hearts, .clubs]il compilatore non dirà nulla e poi quando proverai ad usarlo nel codice, otterrai [TestApp.Suit.spades, TestApp.Suit.hearts, TestApp.Suit.clubs]- questo era il mio punto - che se hai a che fare con un grosso enum e devi aggiungere o rimuovere casi di volta in volta, la tua soluzione è soggetta a errori di omissione mentre la risposta attuale, sebbene non concisa, è più sicura.
dchakarov,

4

In Swift 3, quando è presente l'enum sottostante rawValue, è possibile implementare il Strideableprotocollo. I vantaggi sono che non vengono create matrici di valori come in alcuni altri suggerimenti e che il ciclo Swift "for in" standard funziona, il che rende una bella sintassi.

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

Ahh, proprio quello che stavo cercando per sostituire ForwardIndexType. Ora le mie iterazioni sembrano buone sul sito di utilizzo ... solo nel modo giusto Swifty.
Andrew Duncan,

4

Questa soluzione raggiunge il giusto equilibrio tra leggibilità e manutenibilità.

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

Secondo me, questa è la soluzione migliore. Quando vedo il codice rapido, soprattutto, la leggibilità non è migliore di objc. Ma potrebbe essere, se i programmatori prestassero maggiore attenzione ai lettori del loro codice. Il loro futuro, per esempio :):
Vilém Kurz,

4

Siamo spiacenti, la mia risposta è stata specifica su come ho usato questo post in quello che dovevo fare. Per coloro che si imbattono in questa domanda, cercando un modo per trovare un caso all'interno di un enum, questo è il modo di farlo (nuovo in Swift 2):

Modifica: minuscolo camelCase è ora lo standard per i valori enum di 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)
        }
    }

Per coloro che si interrogano sull'enumerazione di un enum, le risposte fornite in questa pagina che includono una var / let statica contenente un array di tutti i valori di enum sono corrette. L'ultimo codice di esempio Apple per tvOS contiene esattamente la stessa tecnica.

Detto questo, dovrebbero costruire un meccanismo più conveniente nella lingua (Apple, stai ascoltando?)!


3

L'esperimento è stato: ESPERIMENTO

Aggiungi un metodo alla carta che crea un mazzo completo di carte, con una carta per ogni combinazione di valore e seme.

Quindi, senza modificare o migliorare il codice dato diverso dall'aggiunta del metodo (e senza usare cose che non sono state ancora insegnate), ho trovato questa soluzione:

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

3

Ecco un metodo che uso per iterare enume fornire più tipi di valori da unoenum

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

Questo è l'output:

Il numero Zero in francese: zéro, spagnolo: cero, giapponese: nuru
Il numero uno in francese: un, spagnolo: uno, giapponese: ichi
Il numero due in francese: deux, spagnolo: dos, giapponese: ni
Il numero tre in francese : trois, spagnolo: tres, giapponese: san
Il numero quattro in francese: quatre, spagnolo: cuatro, giapponese: shi
Il numero cinque in francese: cinq, spagnolo: cinco, giapponese: vai
Il numero sei in francese: sei, spagnolo: seis, giapponese: roku
Il numero sette in francese: settembre, spagnolo: siete, giapponese: shichi

Totale di 8 casi

siete in giapponese: shichi


AGGIORNARE

Di recente ho creato un protocollo per gestire l'enumerazione. Il protocollo richiede un enum con un valore grezzo Int:

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

2

Sembra un trucco ma se usi valori grezzi puoi fare qualcosa del genere

enum Suit: Int {  
    case Spades = 0, Hearts, Diamonds, Clubs  
 ...  
}  

var suitIndex = 0  
while var suit = Suit.fromRaw(suitIndex++) {  
   ...  
}  

2

Mentre mi occupo di Swift 2.0questo è il mio suggerimento:

Ho aggiunto il tipo non elaborato a Suit enum

enum Suit: Int {

poi:

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

2

Come con la risposta di @Kametrixom qui, credo che restituire un array sarebbe meglio che restituire AnySequence, poiché puoi avere accesso a tutti i gadget di Array come il conteggio, ecc.

Ecco la riscrittura:

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

2

Un'altra soluzione:

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

2

Questo è un post piuttosto vecchio, di Swift 2.0. Ora ci sono alcune soluzioni migliori che utilizzano le funzionalità più recenti di Swift 3.0 : scorrere un Enum in Swift 3.0

E su questa domanda c'è una soluzione che utilizza una nuova funzionalità di (il non ancora rilasciato mentre scrivo questa modifica) Swift 4.2: Come ottengo il conteggio di una enumerazione Swift?


Ci sono molte buone soluzioni in questo thread e altre, tuttavia alcune sono molto complicate. Mi piace semplificare il più possibile. Ecco una soluzione che può funzionare o meno per esigenze diverse ma penso che funzioni bene nella maggior parte dei casi:

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

Per iterare:

for item in Number.allValues {
    print("number is: \(item)")
}

1
Sembra un sacco di lavoro specifico per l'enum individuale che hai creato - non sono sicuro che return [Number.One.rawValue, Number.Two.rawValue, ...] non sia più pulito, in questo caso .
Scott Austin,

Questo è un post piuttosto vecchio, di Swift 2.0. Ora ci sono alcune soluzioni migliori che utilizzano le nuove funzionalità di swift 3.0: stackoverflow.com/questions/41352594/… E su questa domanda c'è una soluzione che utilizza una nuova funzionalità di (il non ancora rilasciato mentre scrivo questa modifica ) Swift 4.2: stackoverflow.com/questions/27094878/…
Abbey Jackson,

2

Enums hanno toRaw()e fromRaw()metodi. Quindi, se il tuo valore Intnon elaborato è un , puoi iterare dal primo all'ultimo enum:

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

Un problema è che devi testare i valori opzionali prima di eseguire il simpleDescriptionmetodo, quindi impostiamo convertedSuitprima il nostro valore e quindi impostiamo una costante suconvertedSuit.simpleDescription()


2
La domanda originale riguardava un enum di tipo String non Int
cynistersix

2

Ecco il mio approccio suggerito. Non è del tutto soddisfacente (sono molto nuovo in Swift e OOP!) Ma forse qualcuno può perfezionarlo. L'idea è che ogni enum fornisca le proprie informazioni sulla gamma .firste le .lastproprietà. Aggiunge solo due righe di codice a ogni enum: ancora un po 'hard-coded, ma almeno non duplica l'intero set. Richiede che la modifica Suitdell'enum sia un Int come ilRank enum, invece di non tipizzato.

Invece di fare eco all'intera soluzione, ecco il codice che ho aggiunto .all'enum, da qualche parte dopo le dichiarazioni del caso ( Suitenum è simile):

var first: Int { return Ace.toRaw() }
var last: Int { return King.toRaw() }

e il ciclo che ho usato per costruire il mazzo come una matrice di String. (La definizione del problema non indicava come doveva essere strutturato il mazzo.)

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
}

È insoddisfacente perché le proprietà sono associate a un elemento piuttosto che all'enum. Ma aggiunge chiarezza ai loop 'for'. Vorrei che dicesse Rank.firstinvece diRank.Ace.first . Funziona (con qualsiasi elemento), ma è brutto. Qualcuno può mostrare come elevarlo a livello di enum?

E per farlo funzionare, ho tolto il createDeckmetodo dalla struttura della Card. Non sono riuscito a capire come ottenere un array [String] restituito da quella struttura, e sembra comunque un brutto posto per mettere un metodo del genere.


2

L'ho fatto usando la proprietà calcolata, che restituisce la matrice di tutti i valori (grazie a questo post http://natecook.com/blog/2014/10/loopy-random-enum-ideas/ ). Tuttavia, utilizza anche int valori grezzi, ma non è necessario ripetere tutti i membri dell'enumerazione in proprietà separate.

UPDATE Xcode 6.1 ha cambiato un po 'il modo in cui ottenere il membro enum usando rawValue, quindi ho corretto l'elenco. Risolto anche un piccolo errore con prima sbagliato rawValue.

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

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

Basato sulla risposta di Rick: questo è 5 volte più veloce


L'aggiunta di un Countcaso interromperà switchimplementazioni e CaseIterableconformità esaustive .
Cœur
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.