Wie vergleiche ich Enum mit zugehörigen Werten, indem ich den zugehörigen Wert in Swift ignoriere?

87

Nachdem ich gelesen habe, wie man die Gleichheit von Swift-Aufzählungen mit zugehörigen Werten testet , habe ich die folgende Aufzählung implementiert:

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

Der folgende Code funktioniert:

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

Dies wird jedoch nicht kompiliert:

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

... und es wird folgende Fehlermeldung angezeigt:

Der Binäroperator '==' kann nicht auf Operanden vom Typ 'CardRank' und '(Int) -> CardRank' angewendet werden.

Ich gehe davon aus, dass dies daran liegt, dass ein vollständiger Typ erwartet wird und CardRank.Numbernicht der gesamte Typ angegeben wird, wohingegen CardRank.Number(2)dies der Fall ist . In diesem Fall möchte ich jedoch, dass es mit einer beliebigen Zahl übereinstimmt . nicht nur eine bestimmte.

Natürlich kann ich eine switch-Anweisung verwenden, aber der Sinn der Implementierung des ==Operators bestand darin, diese ausführliche Lösung zu vermeiden:

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

Gibt es eine Möglichkeit, eine Aufzählung mit zugeordneten Werten zu vergleichen, während der zugehörige Wert ignoriert wird?

Hinweis: Mir ist klar, dass ich den Fall in der ==Methode in ändern könnte case (.Number, .Number): return true, aber obwohl er korrekt zurückgegeben wird, sieht mein Vergleich immer noch so aus, als würde er mit einer bestimmten Zahl ( number == CardRank.Number(2)wobei 2 ein Dummy-Wert ist) und nicht mit einer beliebigen Zahl verglichen ( number == CardRank.Number).

Sinnvoll
quelle
1
Sie können die reduzieren Jack, Queen, King, AceFälle in der ==Operatorimplementierung nur:case (let x, let y) where x == y: return true
Alexander - wieder einzusetzen Monica

Antworten:

72

Bearbeiten: Wie Etan betont, können Sie die (_)Platzhalterübereinstimmung weglassen , um diese sauberer zu verwenden.


Leider glaube ich nicht, dass es switchin Swift 1.2 einen einfacheren Weg gibt als Ihren Ansatz.

In Swift 2 können Sie jedoch die neue if-caseMusterübereinstimmung verwenden:

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

Wenn Sie Ausführlichkeit vermeiden möchten, können Sie isNumberIhrer Aufzählung eine berechnete Eigenschaft hinzufügen , die Ihre switch-Anweisung implementiert.

Ronald Martin
quelle
1
Dies ist ideal für den Kontrollfluss, aber es ist irgendwie zum Kotzen, wenn Sie nur einen einfachen Ausdruck wünschen, z assert(number == .Number). Ich kann nur hoffen, dass dies in späteren Versionen von Swift verbessert wird. = /
Jeremy
3
Saugt auch für Dinge wie while-Schleifenbedingungen usw. In Swift 3 können Sie das (_) für einen saubereren Code entfernen.
Etan
Danke @Etan, das habe ich der Antwort hinzugefügt. Sie können den Platzhalter auch in Swift 2 weglassen. Diese Antwort wurde geschrieben, bevor die Sprachfunktion veröffentlicht wurde, also wusste ich das noch nicht! :-D
Ronald Martin
Außerdem: Wenn eine Aufzählung einen Einzelfall mit zugeordneten Werten enthält, muss das If-Case-Muster auch für Fälle verwendet werden, denen keine Werte zugeordnet sind.
Etan
1
@PeterWarbo, Sie können mit dieser Mustervergleichssyntax keine Negation durchführen. Sie müssen vorerst auf einen defaultFall in einem switchBlock zurückgreifen .
Ronald Martin
28

Leider gibt es in Swift 1.x keinen anderen Weg, den Sie verwenden müssen, switchder nicht so elegant ist wie die Version von Swift 2, in der Sie Folgendes verwenden können if case:

if case .Number = number {
    //ignore the value
}
if case .Number(let x) = number {
    //without ignoring
}
Qbyte
quelle
3
Leider funktioniert dies nur in ifAussagen, nicht als Ausdruck.
Raphael
20

In Swift wird 4.2 Equatablesynthetisiert, wenn alle zugehörigen Werte übereinstimmen Equatable. Alles was Sie tun müssen, ist hinzufügen 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
quelle
18
Dadurch werden jedoch die zugehörigen Werte verglichen.
Joe
3

Hier ist ein einfacherer Ansatz:

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

Der Operator == muss nicht überladen werden, und die Überprüfung des Kartentyps erfordert keine verwirrende Syntax:

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
quelle
3
Obwohl es die übergeordnete Frage nicht beantwortet, ist IMO eine elegantere Lösung (abgesehen von den aufgezählten Zahlen) in Szenarien, die OPs ähneln, und sollte nicht herabgestuft werden.
Nathan Hosselton
0

Du brauchst nicht func ==oder Equatable. Verwenden Sie einfach ein Aufzählungsmuster .

let rank = CardRank.Ace
if case .Ace = rank { print("Snoopy") }
Edward Brey
quelle