繁体   English   中英

如何在具有关联值的枚举上与OR运算符进行模式匹配?

[英]How can I pattern match with the OR operator on an enum with associated values?

我有这个枚举:

enum Foo {
    case a(x: Int)
    case b(x: Int)
    case c
    case d
}

foo

let foo = Foo.a(x: 10)

我想检查foo 要么是 abc ,不管是什么x是。

使用switch语句,我可以执行以下操作:

switch foo {
case .a, .b, .c:
    ...
case .d:
    break
}

但是有点长。

我以为我可以在if case做同样的if case

if case .a, .b, .c = foo { ... }

这产生了编译器错误。

然后,我发现了这个问题 ,并尝试了以下方法:

if [Foo.a, Foo.b, Foo.c].contains(foo) { ... }

编译器认为该数组的类型为[Any] ,所以这也不起作用...

除了将其提取为方法并调用该方法外,我该怎么办? Swift 4.2中有什么新功能可以解决此问题?

Swift不支持此操作,因为Foo实例需要模式匹配,因为它们不是Equatable 并且唯一允许多个模式匹配的分隔符是,并且该运算符对应于and运算符,而不能包含or

一种丑陋的方法(我会说是不正确的或令人误解的)是增加对Equatable一致性,并忽略相关的值:

enum Foo: Equatable {
    case a(x: Int)
    case b(x: Int)
    case c
    case d

    static func ==(_ lhs: Foo, _ rhs: Foo) -> Bool {
        switch (lhs, rhs) {
        case (.a, .a): return true
        case (.b, .b): return true
        case (.c, .c): return true
        case (.d, .d): return true
        default: return false
        }
    }
}

然后,您可以执行以下操作:

if [Foo.a(x: 0), Foo.b(x: 0), Foo.c].contains(foo) { ... }

另一种方法是添加index属性,并在测试时使用该属性:

enum Foo {
    case a(x: Int)
    case b(x: Int)
    case c
    case d

    var index: Int {
        switch self {
        case .a: return 0
        case .b: return 1
        case .c: return 2
        case .d: return 3
        }
    }
}

并按照

if [Foo.a(x: 0), Foo.b(x: 0), Foo.c].map({ $0.index }).contains(foo.index) { ... }

两种解决方案都比简单的开关更为冗长,并且只有在需要多次使用时,它们才是可行的。

另外,您可以使用以下方法扩展Array

extension Array where Element == Foo {
    func matchesCase(_ foo: Foo) -> Bool {
        return contains {
            switch ($0, foo) {
            case (.a, .a): return true
            case (.b, .b): return true
            case (.c, .c): return true
            case (.d, .d): return true
            default: return false
            }
        }
    }
}

,并像这样使用它:

if [Foo.a(x: 0), Foo.b(x: 0), Foo.c].matchesCase(foo) { ... }

还有第四个解决方案:)。 添加sameCase函数:

enum Foo {
    case a(x: Int)
    case b(x: Int)
    case c
    case d

    func sameCase(_ foo: Foo) -> Bool {
        switch self {
        // a little bit more verbose, but don't risk missing new cases
        case .a: if case .a = foo { return true } else { return false }
        case .b: if case .b = foo { return true } else { return false }
        case .c: if case .c = foo { return true } else { return false }
        case .d: if case .d = foo { return true } else { return false }
        }
    }
}

用法:

if [Foo.a(x: 0), Foo.b(x: 0), Foo.c].contains(where: foo.sameCase) { ... }
// or
if foo.sameCase(.a(x: 0)) || foo.sameCase(.b(x: 0)) || foo.sameCase(.c) { ... }

如果您打算在多个地方多次重复进行此测试,那么一遍又一遍地复制冗长的版本确实很烦人。 但是,您可以将这部分代码简单地封装在扩展中。

extension Foo {
  var isABorC: Bool {
    switch self {
    case .a, .b, .c:
      return true
    default:
      return false
    }
  }
}

所以现在您的测试变成这样:

if foo.isABorC { ... }

或者,您可以简单地使其成为枚举声明的一部分:

enum Foo {
  case a(x: Int)
  case b(x: Int)
  case c
  case d

  var isABorC: Bool {
    switch self {
    case .a, .b, .c:
      return true
    case .d:
      return false
    }
  }
}

有一个例子是swift(4.2)文档,该文档使用嵌套枚举来实现一副纸牌的等级,其中可能会添加isFaceCard var。

底线是:您不需要无休止地重复这段文字ad nauseam 您可以隐藏它,直到找到更优雅的解决方案为止。

不幸的是,没有其他办法。

这是因为Foo.a的类型为(Int)-> Foo。 之所以不能使用array.contains,是因为Closure和Foo是不同的类型,因此编译器假定您想要的是Any数组。

要亲自查看这种现象,请尝试以下代码:

print(type(of: Foo.a))

您将得到(Int) -> Foo

暂无
暂无

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

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