简体   繁体   English

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

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

After reading How to test equality of Swift enums with associated values , I implemented the following enum:在阅读了如何测试 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
    }
}

The following code works:以下代码有效:

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

However, this doesn't compile:但是,这不会编译:

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

... and it gives the following error message: ...它给出了以下错误消息:

Binary operator '==' cannot be applied to operands of type 'CardRank' and '(Int) -> CardRank'二元运算符'=='不能应用于'CardRank'和'(Int) -> CardRank'类型的操作数

I'm assuming this is because it's expecting a full type and CardRank.Number does not specify an entire type, whereas CardRank.Number(2) did.我假设这是因为它需要一个完整的类型,而CardRank.Number没有指定一个完整的类型,而CardRank.Number(2)却指定了一个完整的类型。 However, in this case, I want it to match any number;但是,在这种情况下,我希望它匹配任何数字; not just a specific one.不只是一个特定的。

Obviously I can use a switch statement, but the whole point of implementing the == operator was to avoid this verbose solution:显然我可以使用 switch 语句,但实现==运算符的全部目的是避免这种冗长的解决方案:

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

Is there any way to compare an enum with associated values while ignoring its associated value?有没有办法在忽略其关联值的同时将枚举与关联值进行比较?

Note: I realize that I could change the case in the == method to case (.Number, .Number): return true , but, although it would return true correctly, my comparison would still look like its being compared to a specific number ( number == CardRank.Number(2) ; where 2 is a dummy value) rather than any number ( number == CardRank.Number ).注意:我意识到我可以将==方法中的大小写更改为case (.Number, .Number): return true ,但是,尽管它会正确返回 true ,但我的比较仍然看起来像是在与特定数字进行比较( number == CardRank.Number(2) ; 其中 2 是一个虚拟值)而不是任何数字( number == CardRank.Number )。

Edit: As Etan points out, you can omit the (_) wildcard match to use this more cleanly.编辑:正如 Etan 指出的那样,您可以省略(_)通配符匹配以更干净地使用它。


Unfortunately, I don't believe that there's an easier way than your switch approach in Swift 1.2.不幸的是,我认为没有比 Swift 1.2 中的switch方法更简单的方法。

In Swift 2, however, you can use the new if-case pattern match:但是,在 Swift 2 中,您可以使用新的if-case模式匹配:

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

If you're looking to avoid verbosity, you might consider adding an isNumber computed property to your enum that implements your switch statement.如果您希望避免冗长,您可以考虑将isNumber计算属性添加到实现 switch 语句的枚举中。

Unfortunately in Swift 1.x there isn't another way so you have to use switch which isn't as elegant as Swift 2's version where you can use if case :不幸的是,在 Swift 1.x 中没有其他方法,因此您必须使用switch ,它不如 Swift 2 的版本那么优雅,您可以if case使用:

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

In Swift 4.2 Equatable will be synthesized if all your associated values conform to Equatable .在 Swift 4.2 中,如果所有关联的值都符合EquatableEquatable将被合成。 All you need to do is add 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 https://developer.apple.com/documentation/swift/equatable?changes=_3

Here's a simpler approach:这是一个更简单的方法:

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

There's no need to overload the == operator, and checking for card type does not require confusing 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!")
}

I didn't want to conform Equatable (it didn't help me either) and I wanted to filter for other cases than a specific one, so instead of simply writing card != .Number I had to write the following.我不想符合Equatable (它也没有帮助我)并且我想过滤除特定情况以外的其他情况,所以不是简单地写card != .Number我必须写以下内容。 (I adjusted my code to this question.) (我根据这个问题调整了我的代码。)

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

So I can write not a number in a complex condition:所以我不能在复杂的条件下写一个数字

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

I wish I could just write card != .Number , but the compiler was always complaining with Type of expression is ambiguous without more context.我希望我可以只写card != .Number ,但编译器总是抱怨表达式类型不明确,没有更多上下文。 Maybe in an upcoming swift version!也许在即将推出的 swift 版本中!

You don't need func == or Equatable .您不需要func ==Equatable Just use an enumeration case pattern .只需使用枚举案例模式

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

What I usually do to compare if two enum cases "match" no matter their associated value is:如果两个枚举案例“匹配”,无论它们的关联值如何,我通常会比较:

I have a protocol Matchable :我有一个协议Matchable

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

Then I make enums conform to it:然后我让枚举符合它:

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

From Swift 5.3, you can use the Comparable Enums feature:从 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]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM