Como enumerar uma enumeração com o tipo String?


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

Por exemplo, como posso fazer algo como:

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

Exemplo resultante:



Em que caso você não saberia o tipo?
precisa saber é o seguinte

Você está certo, neste caso, é do tipo String.
Lucien

Ainda não há reflexo no Swift ...
Sulthan

22
Não é irônico que eles sejam chamados de enumerações, mas são tão irritantes para enumerar em Swift?
Charlton Provatas

3
@CharltonProvatas Se essa fosse a única desvantagem em Swift, eu chamaria isso de dia. Olhando quantas pessoas oferecem soluções diferentes para isso, estou apenas roendo meu teclado.
21418 Qwerty_so

Respostas:


267

Swift 4.2+

Começando com o Swift 4.2 (com o Xcode 10), basta adicionar a conformidade do protocolo para CaseIterablese beneficiar allCases. Para adicionar essa conformidade ao protocolo, basta escrever em algum lugar:

extension Suit: CaseIterable {}

Se a enum é sua, você pode especificar a conformidade diretamente na declaração:

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

Em seguida, o código a seguir imprimirá todos os valores possíveis:

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

Compatibilidade com versões anteriores do Swift (3.xe 4.x)

Se você precisar oferecer suporte ao Swift 3.x ou 4.0, poderá imitar a implementação do Swift 4.2 adicionando o seguinte código:

#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 Eu ficaria feliz em ajudar, mas: (1) você tem certeza de que recebeu a versão mais recente do código? (alguma falha foi corrigida há um mês) e (2) forneça um MCVE do seu tipo personalizado que possa reproduzir uma falha e sua versão do Xcode.
Cœur

Isso funciona bem para mim para compilações de depuração, mas assim que eu crio uma versão e carrego no TestFlight, ela falha. A Apple está de alguma forma eliminando isso?
Daniel Wood

1
Parece que sua versão tem um positivo em relação à versão embutida do CaseIterator. Posso estender enumerações definidas em outro arquivo com sua versão. Se você tornar allCases na extensão pública, também poderá estender enumerações definidas em diferentes estruturas.
Adamfowlerphoto

1
@CyberMew Atualizei a resposta para esclarecê-la. O livro Swift e as notas de versão do Xcode 10 mencionam CaseIterable são posteriores à minha resposta e usaram exemplos simplificados em que o enum não é suportado String, em oposição à presente questão do Stack Overflow.
Cœur

1
Eu gostaria de enfatizar a importância do "# if! Swift (> = 4.2)". Se você escreveu seu código antes do swift 4.2 e esqueceu de remover o código abaixo "# if! Swift (> = 4.2)", ao usar o Xcode Versão 11.4 para criar localmente no seu dispositivo de teste, tudo ficará bem. Porém, quando o aplicativo é baixado da loja de aplicativos ou do voo de teste, esse código trava o aplicativo. Esse tipo de bug é muito difícil de detectar ou depurar.
Oliver Zhang

524

Esta publicação é relevante aqui https://www.swift-studies.com/blog/2014/6/10/enumerating-enums-in-swift

Essencialmente, a solução 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
Bom, mas ... você deve inserir seus elementos da enumeração duas vezes - uma vez para a enumeração, uma vez para todos os valores. Não é exatamente tão elegante quanto se poderia desejar.
Jay Imerman

2
Concorde com o "mas" ... no entanto, como afirmado no artigo, talvez exista uma questão de que um enum seja realmente um conjunto e, portanto, não ordenado ... lembre-se ... os casos de pedidos definidos em não seriam um começo ruim!
rougeExciter

3
Em Java, o compilador faz isso por você, talvez o Swift 2.0 faça isso também. Em particular em Java, todas as enumerações obtêm um método de descrição (toString em Java) que fornece a String como os nomes dos casos (arruelas, ...) e um conjunto de casos é criado automaticamente. Java também fornece indexação posicional. Como eu disse, talvez o Swift 2.0.
Howard Lovatt

1
Idealmente, você teria algo semelhante à implementação de c # na qual você pode fazer, Enum.Values(typeof(FooEnum))mas exposto como um método de extensão (como mapear ou reduzir). FooEnum.values() :: values(EnumType -> [EnumType])
Rodrigoelp 25/08/14

1
O artigo faz uma boa observação ao final sobre como fazer com que cada valor de enum esteja na matriz allValues ​​com um teste de unidade. No entanto, ainda vejo alguém adicionando mais elementos, mas não aplicando-os no teste de unidade, que ainda nos deixa de volta no início, sem saber ao certo que todos os valores de enumeração são mantidos em todos os valores.
precisa saber é o seguinte

278

Eu criei uma função de utilitário iterateEnum()para iterar casos para enumtipos arbitrários .

Aqui está o exemplo de uso:

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

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

Quais saídas:

♠
♥
♦
♣

Porém, isso é apenas para fins de depuração ou teste : isso depende de vários comportamentos não documentados do compilador Swift1.1; portanto, use-o por sua conta e risco.

Aqui está o código:

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

A ideia subjacente é:

  • A representação de memória de enum, excluindo enums com tipos associados, é apenas um índice de casos em que a contagem dos casos é 2...256idêntica a UInt8quando 257...65536, UInt16e assim por diante. Portanto, pode ser unsafeBitcastde tipos inteiros não assinados correspondentes.
  • .hashValue de valores enum é o mesmo que o índice do caso.
  • .hashValuedos valores de enumeração bitcasted de índice inválido é 0.

Revisado para Swift2 e implementado idéias de elenco da resposta de @ 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
    }
}

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

Revisado para 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
Impressionante e a única resposta que responde à pergunta! Mas sim ... não vou tocar! +1 por esforço!
dotToString 12/03/2015

Acabei de publicar minha resposta que funciona basicamente da mesma maneira (só vi essa resposta mais tarde). Ele usa o Swift 2.0 beta 6 e os recursos modernos do idioma.
Kametrixom 6/09/2015

2
A versão Swift 3 funciona bem. Só precisava modificar o uso um pouco: para f em iterateEnum (Suit.self) {print (f.rawValue)}
Gene De Lisa

16
+1 isso é bastante brilhante. Também é, IMHO, inteligente demais para usar, como evidenciado por quebrar significativamente em todas as principais mudanças de versão do Swift. O crédito do autor, a versão Swift 3 foi feito um mês antes Swift 3 saiu do beta ... Se você estiver indo para tomar esta resposta e aprender tudo isso withUnsafePointer withMemoryRebounde pointeecoisas, então usar este por todos os meios. Caso contrário, eu evitaria.
Dan Rosenstark 01/12/19

5
Eu só quero acrescentar que agora está quebrado no swift 4, mas apenas no linux, então marque com +1 os comentários acima que isso é muito inteligente para usar.
Andrew Plummer

130

As outras soluções funcionam, mas todas elas assumem, por exemplo, o número possível de classificações e naipes, ou qual pode ser o primeiro e o último rank. É verdade que o layout de um baralho de cartas provavelmente não mudará muito no futuro próximo. Em geral, no entanto, é melhor escrever código que faça o mínimo de suposições possível. Minha solução:

Eu adicionei um tipo bruto à Suitenumeração, para que eu possa usar Suit(rawValue:)para acessar os Suitcasos:

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

Abaixo a implementação do createDeck()método do cartão . init(rawValue:)é um inicializador disponível e retorna um opcional. Ao desembrulhar e verificar seu valor nas instruções while, não há necessidade de assumir o número Rankou Suitcasos:

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

Aqui está como chamar o createDeckmétodo:

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

12
Melhor resposta absoluta que já vi em vários tópicos sobre este tópico. Muito elegante. Isso funciona com enumerações do tipo Int, mas eu me pergunto como é possível iterar através de outros tipos (string, tipos personalizados, etc.).
Jay Imerman

3
Esta é definitivamente a melhor solução. Uma coisa a notar. No exemplo do livro, ele não possui "case Spades = 1" como o sdduursma. Eu não entendi isso no começo. que é uma opção, ou você pode simplesmente usar "var m = 0"
Joshua Goossen

10
Isso pressupõe que os valores brutos sejam seqüenciais. Quando isso não é verdade, por exemplo, quando o enum representa sinalizadores de máscara de bits, o loop sai prematuramente.
21714 Jim

1
Esta solução assume que você pode modificar a definição de Suit. Você pode neste exemplo, mas o exercício foi feito para ajudá-lo a trabalhar com o que enumslhe foi dado como se fosse de uma fonte externa.
21715 Josh J

1
A única coisa que quero reclamar é que não posso chamá-lo como método estático, mas preciso criar um objeto de cartão primeiro.
qed

76

Eu tropecei nos bits e bytes e criei uma extensão que depois descobri que funciona muito semelhante à resposta do @rintaro . É usado assim:

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

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

O que é notável é que é utilizável em qualquer enumeração sem valores associados. Observe que isso não funciona para enumerações sem casos.

Como na resposta de @rintaro , esse código usa a representação subjacente de uma enumeração. Essa representação não está documentada e pode mudar no futuro, o que a quebraria. Eu não recomendo o uso disso na produção.

Código (Swift 2.2, Xcode 7.3.1, não está funcionando no 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
            }
        }
    }
}

Código (Swift 3, Xcode 8.1, não funciona no 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
            }
        }
    }
}

Não tenho idéia do porquê preciso typealias, mas o compilador reclama sem ele.


10
Esta resposta é ainda melhor do que a minha resposta, especialmente na peça da carcaça :)
Rintaro

Mas acho que isso funciona apenas em um ambiente endian pequeno?
Rintaro

5
O Xcode 8 beta 6 mudou isso de novo! Eu recebo o seguinte erro: 'init' está indisponível: use 'withMemoryRebound (to: capacity: _)' para exibir temporariamente a memória como outro tipo compatível com o layout. ''
Vorlon confuso

1
@ConfusedVorlon: ver resposta acima por @Rintaro: substituir withUnsafePointer... pointee}porwithUnsafePointer(to: &i) { $0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee } }
Stefan

1
Isso parece não funcionar mais no Xcode 10. (eu sei que isso não seria necessário no Swift 4.2), mas ao usar o Swift 4 (.1) no Xcode 10, esse código não está mais funcionando (o valor bruto é desigual)
benrudhart

26

Você pode iterar através de uma enumeração implementando o ForwardIndexTypeprotocolo.

O ForwardIndexTypeprotocolo requer que você defina uma successor()função para percorrer os elementos.

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

A iteração em um intervalo aberto ou fechado ( ..<ou ...) chamará internamente a successor()função que permite que você escreva isso:

// 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
Acho que essa é a resposta mais "adequada" para a pergunta, mesmo a mais "elegante" (código extra necessário para outras opções aqui existentes), dada a sintaxe resultante quando usada dentro de um intervalo (a sintaxe é o que eu esperaria poder fazer isso se enumerações onde enumeráveis ​​sem soluções alternativas). Obrigado! Embora seja interessante notar que, se a sobrecarga do operador for empregada em outro lugar além do sucessor () (o que parece tentador), obviamente o desembrulhamento forçado é perigoso. Além disso, o infix parece desnecessário ...?
iOS Gamer

Resposta atualizado para refletir mais recentes especificações de idioma Swift
RndmTsk

Um successor()método definido corretamente (primeira opção) eliminaria a necessidade de enumter um tipo associado. +1
nhgrif

1
Mas essa resposta elegante não funcionará para enumerações de String, certo?
Ali

A maioria das soluções "adequadas" / melhores práticas! + 1-ed
Siu Ching Pong -Asuka Kenji-

18

Esse problema agora é muito mais fácil. Aqui está a minha solução 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
}

Pré 4.2:

Eu gosto dessa solução que montei depois de encontrar " Compreensão da lista no Swift ".

Ele usa Int raws em vez de Strings, mas evita digitar duas vezes, permite personalizar os intervalos e não codifica os valores brutos.

Esta é uma versão do Swift 4 da minha solução original, mas veja a melhoria 4.2 acima:

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, vejo agora que o meu é basicamente o mesmo que o de Sutean Rutjanalard.
Adazacom 06/12/2015

1
Na verdade, gostei mais da sua implementação. Eu acho que é mais claro! 1 voto positivo. Na verdade, as respostas mais votadas são muito inteligentes e certamente serão quebradas no futuro. O seu promete alguma estabilidade no futuro.
Jvarela

17

Em princípio, é possível fazê-lo dessa maneira, assumindo que você não use a atribuição de valores brutos para os casos de 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
Isso é bom, mas ele só funciona para enums inteiros contínuos a partir de 0
Robert

@ Robert, como meus comentários estados acima: "você não usar valores cru atribuição para casos de enum"
Alfa07

Sim - não use valores brutos, além de definir o tipo subjacente como int. No Swift, você não precisa de um tipo de enumeração, como no exemplo de naipes. enum ItWontWorkForThisEnum {case a, b, c}
Robert

Como isso solucionaria o problema se uma tupla estiver associada ao caso de enumeração?
RougeExciter

Você não pode associar uma tupla a uma enumeração com muita facilidade.
Nhgrif

13

Se você der ao enum um valor Int bruto, isso tornará o loop muito mais fácil.

Por exemplo, você pode usar anyGeneratorpara obter um gerador que possa enumerar seus valores:

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

No entanto, isso parece um padrão bastante comum, não seria bom se pudéssemos enumerar qualquer tipo de enum simplesmente em conformidade com um protocolo? Bem, com o Swift 2.0 e extensões de protocolo, agora podemos!

Basta adicionar isso ao seu projeto:

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

Agora, sempre que você criar uma enumeração (desde que tenha um valor bruto Int), você poderá torná-la enumerável conforme o protocolo:

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

Se seus valores de enumeração não começarem com 0(o padrão), substitua o firstRawValuemétodo:

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

A classe final do Suit, incluindo a substituição simpleDescriptionpelo protocolo CustomStringConvertible , mais padrão , terá a seguinte aparência:

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

Sintaxe do 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 ++ será removido em rápida 3. O que você sugere como substituto para - var nextIndex = firstRawValue () return anyGenerator {Auto (RawValue: nextIndex ++)}
Avi

Descobri isso. adie {nextIndex + = 1} retorne AnyGenerator {Self (rawValue: nextIndex)}
Avi

12

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

É um código atualizado para o formulário Swift 2.2 , resposta de Kametrixom

Para Swift 3.0+ (muito obrigado 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 você poderia explicar o que quer dizer com isso?
ale_stro

Opa, desculpe, eu verifiquei novamente e houve um bug no playground: no projeto real esse código funciona como esperado, obrigado! =)
silvansky

1
Ótima solução! Além disso, funciona bem no Swift 3 com alterações mínimas ('AnyGenerator' renomeado 'AnyIterator' e '.memory' renomeado para '.pointee').
Philip

9

Solução 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

Eu me vi fazendo .allValuesmuito em todo o meu código. Finalmente descobri uma maneira de simplesmente estar em conformidade com um Iteratableprotocolo e ter um rawValues()método.

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

Chame-o

print(Filter.allValues)

Impressões:

["Salário", "Experiência", "Tecnologia", "Não utilizado", "Alto valor não utilizado"]


versões mais antigas

Por enumrepresentarInt

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

Chame assim:

print(Filter.allValues)

Impressões:

[0, 1, 2, 3, 4]


Por enumrepresentarString

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

Chame-o

print(Filter.allValues)

Impressões:

["Salário", "Experiência", "Tecnologia", "Não utilizado", "Alto valor não utilizado"]


7

EDIT: Proposta de evolução rápida SE-0194 A coleção derivada de casos de enum propõe uma solução equilibrada para este problema. Vemos isso no Swift 4.2 e mais recente. A proposta também aponta para algumas soluções alternativas que são semelhantes às já mencionadas aqui, mas pode ser interessante ver, no entanto.

Também manterei minha postagem original por uma questão de integridade.


Essa é outra abordagem baseada na resposta de @ Peymmankh, adaptada ao 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
    }
}

Que tal agora?


Obrigado, funciona como eu preciso. Mas existe alguma oportunidade no fechamento do mapa para obter o valor não pelo índice, mas pelo nome?
Mikhail

4

Você pode tentar enumerar assim

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
Isso é muito código inútil! É equivalente a static var enumerate = [Mercury, Venus, Earth, Mars], tornando-a uma resposta subpar em comparação com mais votado resposta stackoverflow.com/a/24137319/1033581
Cœur

@ Nossa resposta tem o importante benefício de usar o compilador para garantir que você não perca um caso.
Dchakarov 17/05/19

@ Cœur que tem o mesmo problema de permitir que você faça um erro do usuário, ou seja, o compilador não vai reclamar se você escrever return [Mercury, Venus, Mars]em vez dereturn [Mercury, Venus, Earth, Mars]
dchakarov

@dchakarov Decidi postar a melhoria como uma resposta, para maior clareza: stackoverflow.com/a/50409525/1033581
Cœur

@ Cœur Se na sua nova resposta você substituir a declaração de retorno por esta, return [.spades, .hearts, .clubs]o compilador não dirá nada e, quando tentar usá-la no código, você obterá [TestApp.Suit.spades, TestApp.Suit.hearts, TestApp.Suit.clubs]- esse foi o meu ponto - de que, se você estiver lidando com um grande problema enum e você deve adicionar ou remover casos de tempos em tempos, sua solução é propensa a erros de omissão, enquanto a resposta atual, embora não seja concisa, é mais segura.
Dchakarov 18/05/19

4

No Swift 3, quando o enum subjacente tem rawValue , você poderá implementar o Strideableprotocolo. As vantagens são que nenhuma matriz de valores é criada, como em outras sugestões, e que o loop Swift "for in" padrão funciona, o que cria uma boa sintaxe.

// "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, exatamente o que eu estava procurando para substituir o ForwardIndexType. Agora, minhas iterações ficam bem no site de uso ... da maneira Swifty adequada.
Andrew Duncan

4

Esta solução atinge o equilíbrio certo de legibilidade e manutenção.

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

Na minha opinião, esta é a melhor solução. Quando vejo código rápido, principalmente, a legibilidade não é melhor que objc. Mas poderia ser, se os programadores prestassem mais atenção aos leitores de seu código. O futuro deles, por exemplo :)
Vilém Kurz

4

Desculpe, minha resposta foi específica de como eu usei este post no que eu precisava fazer. Para aqueles que se deparam com essa pergunta, procurando uma maneira de encontrar um caso dentro de uma enumeração, esta é a maneira de fazê-lo (novo em Swift 2):

Edit: lowercase camelCase agora é o padrão para os valores de enumeração do 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)
        }
    }

Para aqueles que se perguntam sobre a enumeração em uma enumeração, as respostas fornecidas nesta página que incluem uma var / let estática contendo uma matriz de todos os valores de enumeração estão corretas. O último exemplo de código da Apple para tvOS contém exatamente essa mesma técnica.

Dito isto, eles devem criar um mecanismo mais conveniente para o idioma (Apple, você está ouvindo?)!


3

O experimento foi: EXPERIMENT

Adicione um método ao cartão que crie um baralho completo, com um cartão de cada combinação de valor e naipe.

Portanto, sem modificar ou aprimorar o código fornecido, além de adicionar o método (e sem usar coisas que ainda não foram ensinadas), eu vim com esta solução:

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

Aqui está um método que eu uso para iterar enume fornecer vários tipos de valores de umenum

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

Esta é a saída:

O número Zero em francês: zéro, espanhol: cero, japonês: nuru
O número um em francês: un, espanhol: uno, japonês: ichi
O número dois em francês: deux, espanhol: dos, japonês: ni
O número três em francês : trois, espanhol: tres, japonês: san
O número quatro em francês: quatre, espanhol: cuatro, japonês: shi
O número cinco em francês: cinq, espanhol: cinco, japonês: go
O número seis em francês: seis, espanhol: seis, japonês: roku
O número sete em francês: sept, espanhol: siete, japonês: shichi

Total de 8 casos

siete em japonês: shichi


ATUALIZAR

Eu criei recentemente um protocolo para lidar com a enumeração. O protocolo requer uma enumeração com um valor bruto 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

Parece um hack, mas se você usar valores brutos, poderá fazer algo assim

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

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

2

Enquanto lida com Swift 2.0aqui é a minha sugestão:

Eu adicionei o tipo bruto a Suit enum

enum Suit: Int {

então:

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

Tal como acontece com a @Kametrixom, responda aqui , acredito que retornar um array seria melhor do que retornar o AnySequence, já que você pode ter acesso a todos os presentes do Array, como count, etc.

Aqui está a reescrita:

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

Outra solução:

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

Este é um post bastante antigo, do Swift 2.0. Agora, existem algumas soluções melhores aqui que usam recursos mais recentes do swift 3.0: Iterando através de um Enum no Swift 3.0

E nesta questão há uma solução que usa um novo recurso (o ainda não lançado, enquanto escrevo esta edição) Swift 4.2: Como obtenho a contagem de uma enumeração Swift?


Existem muitas boas soluções nesse segmento e outras, no entanto, algumas delas são muito complicadas. Eu gosto de simplificar o máximo possível. Aqui está uma solução que pode ou não funcionar para diferentes necessidades, mas acho que funciona bem na maioria dos casos:

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

Para iterar:

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

1
Parece um monte de trabalho específico para a enum individual que você criou - não tenho certeza de que o retorno [Number.One.rawValue, Number.Two.rawValue, ...] não seja mais limpo, neste caso .
Scott Austin

Este é um post bastante antigo, do Swift 2.0. Agora, aqui estão algumas soluções melhores que usam os recursos mais recentes do swift 3.0: stackoverflow.com/questions/41352594/… E nesta questão, existe uma solução que usa um novo recurso (o ainda não lançado enquanto escrevo esta edição). ) Swift 4.2: stackoverflow.com/questions/27094878/…
Abbey Jackson

2

Enums têm toRaw()e fromRaw()métodos. Portanto, se seu valor bruto for um Int, é possível iterar do primeiro ao último 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()
    }
}

Uma dica é que você precisa testar valores opcionais antes de executar o simpleDescriptionmétodo, portanto, definimos convertedSuitnosso valor primeiro e, em seguida, definimos uma constante comoconvertedSuit.simpleDescription()


2
A pergunta original era sobre um tipo de enumeração de Cordas não Int
cynistersix

2

Aqui está minha abordagem sugerida. Não é completamente satisfatório (eu sou muito novo no Swift e OOP!), Mas talvez alguém possa refinar isso. A idéia é fazer com que cada enum forneça suas próprias informações de intervalo como .firste .lastpropriedades. Ele adiciona apenas duas linhas de código a cada enumeração: ainda um pouco codificado, mas pelo menos não está duplicando todo o conjunto. Requer a modificação do Suitenum para ser um Int como o Rankenum, em vez de não digitado.

Em vez de ecoar a solução inteira, eis o código que adicionei à .enum, em algum lugar após as instruções de caso (a Suitenum é semelhante):

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

e o loop que eu usei para construir o baralho como uma matriz de String. (A definição do problema não indicava como o baralho deveria ser estruturado.)

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
}

Não é satisfatório porque as propriedades estão associadas a um elemento e não à enumeração. Mas isso adiciona clareza aos loops 'for'. Eu gostaria de dizer em Rank.firstvez de Rank.Ace.first. Funciona (com qualquer elemento), mas é feio. Alguém pode mostrar como elevar isso ao nível de enum?

E para fazê-lo funcionar, tirei o createDeckmétodo da estrutura do cartão. Eu não conseguia descobrir como obter uma matriz [String] retornada dessa estrutura, e isso parece um lugar ruim para colocar esse método de qualquer maneira.


2

Eu fiz isso usando a propriedade computada, que retorna a matriz de todos os valores (graças a este post http://natecook.com/blog/2014/10/loopy-random-enum-ideas/ ). No entanto, ele também usa int raw-values, mas não preciso repetir todos os membros da enumeração em propriedades separadas.

ATUALIZAÇÃO O Xcode 6.1 mudou um pouco a maneira de obter o membro enum usando rawValue, então eu consertei a listagem. Também foi corrigido um pequeno erro com o erro errado primeiro 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
    }
}

Baseado na resposta de Rick: isso é 5 vezes mais rápido


A adição de um Countcaso quebrará switchimplementações e CaseIterableconformidade exaustivas .
Coeur
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.