如何通过忽略Swift中的关联值来比较枚举和关联值?


87

在阅读了如何用关联值测试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,但是,尽管它会正确返回true,但是我的比较仍然看起来像是将其与特定数字(number == CardRank.Number(2);其中2是虚拟值)而不是任何数字进行比较(number == CardRank.Number)。


1
您可以降低JackQueenKingAce在情况下==运营商实施只是:case (let x, let y) where x == y: return true
亚历山大-恢复莫妮卡

Answers:


72

编辑:正如Etan所指出的,您可以省略(_)通配符匹配以更简洁地使用它。


不幸的是,我不认为有比switchSwift 1.2中的方法更简单的方法。

但是,在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中对此进行改进。= /
杰里米(Jeremy)

3
也很糟糕,例如while循环条件等。在Swift 3中,您可以删除(_)以获得更清晰的代码。
Etan

谢谢@Etan,我已将其添加到答案中。实际上,您也可以在Swift 2中省略通配符。这个答案是在语言功能发布之前写的,所以我还不知道!:-D
罗纳德·马丁

另外:如果枚举只有一个带有关联值的个案,则即使对于那些没有关联值的个案,也需要使用if-case模式。
伊坦

1
@PeterWarbo,您不能使用这种模式匹配语法进行否定运算。您现在必须暂时回到一个default案例中switch
罗纳德·马丁

28

不幸的是,在Swift 1.x中并没有其他方法,因此您必须使用switch一种不如Swift 2的版本那么优雅的方法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
尽管它没有回答最重要的问题,但在类似于OP的情况下,IMO还是一个更优雅的解决方案(不包括枚举数字),因此不应被否决。
内森·霍瑟尔顿

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.