Swiftで関連する値を無視して、列挙型を関連する値と比較するにはどうすればよいですか?


90

Swift列挙型と関連する値の同等性をテストする方法を読んだ後、次の列挙型を実装しました。

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

次のコードが機能します。

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

ただし、これはコンパイルされません。

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

...そしてそれは次のエラーメッセージを出します:

二項演算子 '=='は、タイプ 'CardRank'および '(Int)-> CardRank'のオペランドには適用できません。

これは、完全な型を想定しておりCardRank.Number、型全体を指定していないためだと思いますが、CardRank.Number(2)そうでした。ただし、この場合は、任意の数に一致させたいと思います。特定のものだけではありません。

明らかに、switchステートメントを使用できますが、==演算子を実装する目的は、この冗長なソリューションを回避することでした。

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

関連する値を無視しながら、列挙型を関連する値と比較する方法はありますか?

注:==メソッドの大文字と小文字をに変更できることはわかってcase (.Number, .Number): return trueいますが、正しくは返されますが、比較number == CardRank.Number(2)は、任意の数値ではなく、特定の数値(;ここで2はダミー値)と比較されているように見えます。(number == CardRank.Number)。


1
あなたは減らすことができJackQueenKingAceに例を==:だけにオペレータの実装case (let x, let y) where x == y: return true
アレクサンダー

回答:


76

編集: Etanが指摘しているように、(_)ワイルドカードの一致を省略して、これをよりクリーンに使用できます。


残念ながら、Swift1.2でのswitchアプローチよりも簡単な方法があるとは思いません。

ただし、Swift 2では、新しいif-caseパターンマッチを使用できます。

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

冗長性を回避したい場合はisNumber、switchステートメントを実装する列挙型に計算プロパティを追加することを検討してください。


1
これは制御フローには最適ですが、単純な式が必要な場合はちょっと面倒ですassert(number == .Number)。例:。これがSwiftの新しいバージョンで改善されることを願っています。= /
ジェレミー

3
また、whileループ条件などの問題も解決します。Swift3では、(_)を削除してコードを整理できます。
エタン2016年

@Etanに感謝します、私はそれを答えに追加しました。Swift2でも実際にはワイルドカードを省略できます。この回答は言語機能がリリースされる前に書かれたので、私はまだそれを知りませんでした!:-D
ロナルドマーティン

また、列挙型に値が関連付けられた単一のケースがある場合、値が関連付けられていないケースでもif-caseパターンを使用する必要があります。
エタン2016年

1
@PeterWarbo、このパターンマッチング構文で否定を行うことはできません。今のところdefaultswitchブロック内のケースにフォールバックする必要があります。
ロナルドマーティン

29

残念ながら、Swift 1.xには別の方法がないため、使用switchできるSwift2のバージョンほどエレガントではない方法を使用する必要がありますif case

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

3
残念ながら、これはifステートメントでのみ機能し、式としては機能しません。
ラファエル


3

より簡単なアプローチは次のとおりです。

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

==演算子をオーバーロードする必要はなく、カードタイプのチェックに混乱する構文は必要ありません。

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
包括的な質問には答えませんが、IMOは、OPと同様のシナリオでは、より洗練されたソリューション(数値は別として)であり、反対票を投じるべきではありません。
Nathan Hosselton 2017年

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.