【问题标题】:How to compare enum with associated values by ignoring its associated value in Swift?如何通过忽略 Swift 中的关联值来比较枚举与关联值?
【发布时间】:2015-10-11 11:39:28
【问题描述】:

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

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

二元运算符“==”不能应用于“CardRank”和“(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,但是,虽然它会正确返回 true,但我的比较看起来仍然像被比较一个特定的数字(number == CardRank.Number(2);其中 2 是一个虚拟值)而不是任何数字(number == CardRank.Number)。

【问题讨论】:

标签: swift enums comparison


【解决方案1】:

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


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

然而,在 Swift 2 中,您可以使用新的 if-case 模式匹配:

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

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

【讨论】:

  • 这对于控制流来说非常有用,但是当你只想要一个简单的表达式时它有点糟糕,例如:assert(number == .Number)。我只能希望这在以后的 Swift 版本中得到改进。 =/
  • 对于 while 循环条件等也很糟糕。在 Swift 3 中,您可以删除 (_) 以获得更简洁的代码。
  • 感谢@Etan,我已将其添加到答案中。实际上,您也可以在 Swift 2 中省略通配符。这个答案是在语言功能发布之前写的,所以我还不知道! :-D
  • 你能否定 if 的情况吗?似乎不可能做到if case .Number != number
  • @PeterWarbo,你不能用这种模式匹配语法做否定。您现在必须退回到 default 块中的 switch 案例。
【解决方案2】:

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

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

【讨论】:

  • 遗憾的是,这只适用于if 语句,而不是表达式。
【解决方案3】:

在 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

【讨论】:

  • 虽然这会比较相关的值。
【解决方案4】:

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

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

【讨论】:

  • 虽然它没有回答最重要的问题,但在类似于 OP 的场景中,IMO 这是一个更优雅的解决方案(除了枚举数字之外),不应该被否决。
【解决方案5】:

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

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

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

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

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

【讨论】:

    【解决方案6】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多