Como comparar enum com valores associados, ignorando seu valor associado em Swift?


87

Depois de ler Como testar a igualdade de enums do Swift com valores associados , implementei o seguinte enum:

enum CardRank {
    case Number(Int)
    case Jack
    case Queen
    case King
    case Ace
}

func ==(a: CardRank, b: CardRank) -> Bool {
    switch (a, b) {
    case (.Number(let a), .Number(let b))   where a == b: return true
    case (.Jack, .Jack): return true
    case (.Queen, .Queen): return true
    case (.King, .King): return true
    case (.Ace, .Ace): return true
    default: return false
    }
}

O seguinte código funciona:

let card: CardRank = CardRank.Jack
if card == CardRank.Jack {
    print("You played a jack!")
} else if card == CardRank.Number(2) {
    print("A two cannot be played at this time.")
}

No entanto, isso não compila:

let number = CardRank.Number(5)
if number == CardRank.Number {
    print("You must play a face card!")
}

... e dá a seguinte mensagem de erro:

O operador binário '==' não pode ser aplicado a operandos do tipo 'CardRank' e '(Int) -> CardRank'

Estou supondo que isso ocorre porque ele está esperando um tipo completo e CardRank.Numbernão especifica um tipo inteiro, enquanto o CardRank.Number(2)fazia. No entanto, neste caso, quero que corresponda a qualquer número; não apenas um específico.

Obviamente, posso usar uma instrução switch, mas o objetivo principal da implementação do ==operador era evitar esta solução detalhada:

switch number {
case .Number:
    print("You must play a face card!")
default:
    break
}

Existe alguma maneira de comparar um enum com valores associados enquanto ignora seu valor associado?

Observação: eu percebo que poderia alterar o caso no ==método para case (.Number, .Number): return true, mas, embora retornasse verdadeiro corretamente, minha comparação ainda pareceria estar sendo comparada a um número específico ( number == CardRank.Number(2); onde 2 é um valor fictício) em vez de qualquer número ( number == CardRank.Number).


1
Você pode reduzir os Jack, Queen, King, Acecasos na ==implementação operador apenas:case (let x, let y) where x == y: return true
Alexander - Reintegrar Monica

Respostas:


72

Edit: Como Etan aponta, você pode omitir a (_)correspondência curinga para usar isso de forma mais limpa.


Infelizmente, não acredito que haja uma maneira mais fácil do que sua switchabordagem no Swift 1.2.

No Swift 2, no entanto, você pode usar a nova if-casecorrespondência de padrão:

let number = CardRank.Number(5)
if case .Number(_) = number {
    // Is a number
} else {
    // Something else
}

Se você está procurando evitar o detalhamento, pode considerar adicionar uma isNumberpropriedade computada ao seu enum que implemente sua instrução switch.


1
Isso é ótimo para controle de fluxo, mas é meio chato quando você quer apenas uma expressão simples, por exemplo: assert(number == .Number). Só posso esperar que isso seja melhorado nas versões posteriores do Swift. = /
Jeremy

3
Também é péssimo para coisas como condições de loop while etc. No Swift 3 você pode remover o (_) para um código mais limpo.
Etan

Obrigado @Etan, adicionei isso à resposta. Você também pode omitir o curinga no Swift 2. Esta resposta foi escrita antes do lançamento do recurso de linguagem, então eu não sabia disso ainda! :-D
Ronald Martin

Além disso: Se um enum tiver um único caso com valores associados, o padrão if-case precisa ser usado mesmo para os casos que não têm valores associados.
Etan

1
@PeterWarbo, você não pode negar com esta sintaxe de correspondência de padrões. Você terá que defaultrecorrer a um caso em um switchbloco por enquanto.
Ronald Martin

28

Infelizmente, no Swift 1.x não há outra maneira de usar switchque não seja tão elegante quanto a versão do Swift 2, onde você pode usar if case:

if case .Number = number {
    //ignore the value
}
if case .Number(let x) = number {
    //without ignoring
}

3
Infelizmente, isso só funciona em ifdeclarações, não como uma expressão.
Raphael


3

Esta é uma abordagem mais simples:

enum CardRank {
    case Two
    case Three
    case Four
    case Five
    case Six
    case Seven
    case Eight
    case Nine
    case Ten
    case Jack
    case Queen
    case King
    case Ace

    var isFaceCard: Bool {
        return (self == Jack) || (self == Queen) || (self == King)
    }
}

Não há necessidade de sobrecarregar o operador == e a verificação do tipo de cartão não requer sintaxe confusa:

let card = CardRank.Jack

if card == CardRank.Jack {
    print("You played a jack")
} else if !card.isFaceCard {
    print("You must play a face card!")
}

3
Embora não responda à questão geral, IMO esta é uma solução mais elegante (números enumerados à parte) em cenários semelhantes aos OPs e não deve ser rejeitada.
Nathan Hosselton

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.