Come confrontare l'enumerazione con i valori associati ignorando il valore associato in Swift?


90

Dopo aver letto Come testare l'uguaglianza delle enumerazioni Swift con i valori associati , ho implementato la seguente enumerazione:

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

Il codice seguente funziona:

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

Tuttavia, questo non compila:

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

... e fornisce il seguente messaggio di errore:

L'operatore binario "==" non può essere applicato agli operandi di tipo "CardRank" e "(Int) -> CardRank"

Suppongo che ciò sia dovuto al fatto che si aspetta un tipo completo e CardRank.Numbernon specifica un tipo intero, mentre lo ha CardRank.Number(2)fatto. Tuttavia, in questo caso, voglio che corrisponda a qualsiasi numero; non solo uno specifico.

Ovviamente posso usare un'istruzione switch, ma il punto centrale dell'implementazione ==dell'operatore era evitare questa soluzione prolissa:

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

C'è un modo per confrontare un'enumerazione con i valori associati ignorando il suo valore associato?

Nota: mi rendo conto che potrei cambiare il caso nel ==metodo in case (.Number, .Number): return true, ma, anche se restituirebbe vero correttamente, il mio confronto sembrerebbe comunque essere confrontato con un numero specifico ( number == CardRank.Number(2); dove 2 è un valore fittizio) piuttosto che con qualsiasi numero ( number == CardRank.Number).


1
È possibile ridurre i Jack, Queen, King, Acei casi nella ==realizzazione all'operatore di appena:case (let x, let y) where x == y: return true
Alexander

Risposte:


76

Modifica: come sottolinea Etan, puoi omettere la (_)corrispondenza con caratteri jolly per usarla in modo più pulito.


Sfortunatamente, non credo che ci sia un modo più semplice del tuo switchapproccio in Swift 1.2.

In Swift 2, tuttavia, puoi utilizzare la nuova if-casecorrispondenza di pattern:

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

Se stai cercando di evitare la verbosità, potresti prendere in considerazione l'aggiunta di una isNumberproprietà calcolata all'enumerazione che implementa l'istruzione switch.


1
Questo è grande per il flusso di controllo, ma fa schifo un pò quando si desidera solo un'espressione semplice, ad esempio: assert(number == .Number). Posso solo sperare che questo sia migliorato nelle versioni successive di Swift. = /
Jeremy,

3
Fa schifo anche per cose come le condizioni del ciclo while ecc. In Swift 3 puoi rimuovere (_) per un codice più pulito.
Etan

Grazie @Etan, l'ho aggiunto alla risposta. Puoi anche omettere il carattere jolly in Swift 2. Questa risposta è stata scritta prima del rilascio della funzionalità della lingua, quindi non lo sapevo ancora! MrGreen
Ronald Martin

Inoltre: se un'enumerazione ha un singolo caso con valori associati, il modello if-case deve essere utilizzato anche per quei casi che non hanno valori associati.
Etan

1
@PeterWarbo, non puoi eseguire la negazione con questa sintassi di corrispondenza dei modelli. Per ora dovrai ripiegare su un defaultcaso in un switchblocco.
Ronald Martin

29

Sfortunatamente in Swift 1.x non c'è un altro modo quindi devi usare switchche non è così elegante come la versione di Swift 2 dove puoi usare if case:

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

3
Purtroppo, questo funziona solo nelle ifdichiarazioni, non come espressione.
Raphael


3

Ecco un approccio più semplice:

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

Non è necessario sovraccaricare l'operatore == e il controllo del tipo di carta non richiede una sintassi 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
Sebbene non risponda alla domanda generale, IMO questa è una soluzione più elegante (numeri enumerati a parte) in scenari simili agli OP e non dovrebbe essere sottovalutata.
Nathan Hosselton

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.