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

How to compare enum with associated values by ignoring its associated value in Swift?

阅读How to test equality of Swift enums with associated values后,我实现了以下枚举:

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

... 并给出以下错误消息:

Binary operator '==' cannot be applied to operands of type 'CardRank' and '(Int) -> CardRank'

我假设这是因为它需要一个完整的类型,而 CardRank.Number 没有指定完整的类型,而 CardRank.Number(2) 指定了。但是,在这种情况下,我希望它匹配 any 数字;不只是一个特定的。

显然我可以使用 switch 语句,但实现 == 运算符的全部目的是避免这种冗长的解决方案:

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

有什么方法可以在忽略其关联值的情况下将枚举与关联值进行比较吗?

注意: 我意识到我可以将 == 方法中的大小写更改为 case (.Number, .Number): return true,但是,尽管它会 return true 正确,我的比较看起来仍然像是与特定数字(number == CardRank.Number(2);其中 2 是虚拟值)而不是 any 数字(number == CardRank.Number ).

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


不幸的是,我认为没有比 Swift 1.2 中的 switch 方法更简单的方法了。

在 Swift2 中,您可以使用新的 if-case 模式匹配:

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

如果您希望避免冗长,您可以考虑向实现 switch 语句的枚举添加一个 isNumber 计算 属性。

不幸的是,在 Swift 1.x 中没有其他方法,因此您必须使用 switch,它不如 Swift 2 的版本优雅使用 if case:

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

这里有一个更简单的方法:

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

在Swift 4.2 Equatable 中,如果您的所有关联值都符合Equatable,则将被合成。您需要做的就是添加 Equatable.

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

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

您不需要 func ==Equatable。只需使用 enumeration case pattern.

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

我不想符合 Equatable(它也没有帮助我)并且我想过滤特定情况以外的其他情况,所以我不是简单地写 card != .Number不得不写下。 (我针对这个问题调整了我的代码。)

enum CardRank {
    ...
    var isNumber: Bool {
       if case .Number = self { return true }
       return false
    }
}

所以我可以在复杂的条件下写不是数字:

if something && !card.isNumber { ... }

我希望我可以只写 card != .Number,但编译器总是抱怨 表达式类型不明确,没有更多上下文。 也许在即将到来的 [=25] =]版本!

我通常会比较两个枚举案例是否“匹配”,而不管它们的关联值是:

我有协议Matchable:

protocol Matchable {
  static func ~= (lhs: Self, rhs: Self) -> Bool
}

然后我使枚举符合它:

extension CardRank: Matchable {
  static func ~= (lhs: Self, rhs: Self) -> Bool {
    switch (lhs, rhs) {
      case
        (.number, .number),
        (.jack, .jack),
        (.queen, .queen),
        (.king, .king),
        (.ace, .ace):
        return true
        
      default:
        return false
    }
  }
}

let card1: CardRank = .number(1)
let card2: CardRank = .number(2)
let card3: CardRank = .jack

print(card1 ~= card2) // true
print(card1 ~= card3) // false

从 Swift 5.3 开始,您可以使用 Comparable Enums 功能:

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

let cards: [CardRank] = [
    .Queen, .Number(8), .Ace, .Number(3), .King
]

print(cards.sorted())
// [.Number(3), .Number(8), .Queen, .King, .Ace]