Comment comparer enum avec les valeurs associées en ignorant sa valeur associée dans Swift?

87

Après avoir lu Comment tester l'égalité des énumérations Swift avec les valeurs associées , j'ai implémenté l'énumération suivante:

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

Le code suivant fonctionne:

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

Cependant, cela ne compile pas:

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

... et il donne le message d'erreur suivant:

L'opérateur binaire '==' ne peut pas être appliqué aux opérandes de type 'CardRank' et '(Int) -> CardRank'

Je suppose que c'est parce qu'il attend un type complet et CardRank.Numberne spécifie pas un type entier, alors qu'il l'a CardRank.Number(2)fait. Cependant, dans ce cas, je veux qu'il corresponde à n'importe quel nombre; pas seulement un spécifique.

Évidemment, je peux utiliser une instruction switch, mais le but de l'implémentation de l' ==opérateur était d'éviter cette solution verbeuse:

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

Existe-t-il un moyen de comparer une énumération avec les valeurs associées tout en ignorant sa valeur associée?

Remarque: je me rends compte que je pourrais changer la casse de la ==méthode en case (.Number, .Number): return true, mais, même si elle retournerait vrai correctement, ma comparaison ressemblerait toujours à un nombre spécifique ( number == CardRank.Number(2); où 2 est une valeur fictive) plutôt qu'à n'importe quel nombre ( number == CardRank.Number).

Sensé
la source
1
Vous pouvez réduire les Jack, Queen, King, Acecas dans la ==mise en œuvre de l' opérateur juste:case (let x, let y) where x == y: return true
Alexander - Monica Réintégrer

Réponses:

72

Edit: Comme le souligne Etan, vous pouvez omettre la (_)correspondance générique pour l'utiliser plus proprement.


Malheureusement, je ne pense pas qu'il existe un moyen plus simple que votre switchapproche dans Swift 1.2.

Dans Swift 2, cependant, vous pouvez utiliser la nouvelle if-casecorrespondance de modèle:

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

Si vous cherchez à éviter la verbosité, vous pouvez envisager d'ajouter une isNumberpropriété calculée à votre énumération qui implémente votre instruction switch.

Ronald Martin
la source
1
Il est parfait pour le flux de contrôle, mais il suce un peu quand vous voulez juste une simple expression, par exemple: assert(number == .Number). Je ne peux qu'espérer que cela sera amélioré dans les versions ultérieures de Swift. = /
Jeremy
3
Suce aussi pour des choses comme les conditions de boucle while, etc. Dans Swift 3, vous pouvez supprimer le (_) pour un code plus propre.
Etan
Merci @Etan, j'ai ajouté cela à la réponse. Vous pouvez également omettre le caractère générique dans Swift 2. Cette réponse a été écrite avant la sortie de la fonctionnalité de langage, donc je ne le savais pas encore! :-D
Ronald Martin
Aussi: si une énumération a un seul cas avec des valeurs associées, le modèle if-case doit être utilisé même pour les observations qui n'ont pas de valeurs associées.
Etan
1
@PeterWarbo, vous ne pouvez pas faire de négation avec cette syntaxe de correspondance de modèle. Vous devrez vous rabattre sur une defaultaffaire dans un switchbloc pour le moment.
Ronald Martin
28

Malheureusement, dans Swift 1.x, il n'y a pas d'autre moyen, vous devez donc utiliser switchce qui n'est pas aussi élégant que la version de Swift 2 où vous pouvez utiliser if case:

if case .Number = number {
    //ignore the value
}
if case .Number(let x) = number {
    //without ignoring
}
Qbyte
la source
3
Malheureusement, cela ne fonctionne que dans les ifdéclarations, pas comme une expression.
Raphael
20

Dans Swift 4.2 Equatablesera synthétisé si toutes vos valeurs associées sont conformes à Equatable. Tout ce que vous avez à faire est d'ajouter Equatable.

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

https://developer.apple.com/documentation/swift/equatable?changes=_3

nambatee
la source
18
Bien que cela comparera les valeurs associées.
Joe
3

Voici une approche plus simple:

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

Il n'est pas nécessaire de surcharger l'opérateur ==, et la vérification du type de carte ne nécessite pas de syntaxe déroutante:

let card = CardRank.Jack

if card == CardRank.Jack {
    print("You played a jack")
} else if !card.isFaceCard {
    print("You must play a face card!")
}
Mike Taverne
la source
3
Bien que cela ne réponde pas à la question primordiale, l'OMI, c'est une solution plus élégante (numéros énumérés mis à part) dans des scénarios similaires aux OP et ne devrait pas être rejetée.
Nathan Hosselton