【问题标题】:How to do Array pattern matching如何进行数组模式匹配
【发布时间】:2017-10-04 21:33:11
【问题描述】:

我正在寻找这样的东西:

switch array {
case []:
    print("No elements")
case let [x]:
    print(x)
case let [a, b]:
    print(a)
    print(b)
default:
    print("More than 2 elements")
}

有什么方法可以在 Swift 中实现这一点吗?

【问题讨论】:

  • 只需切换阵列数量switch array.count { case 0: print("No elements") case 1: print(array[0]) case 2: print(array[0]) print(array[1]) default: print("More than 2 elements") }
  • 是的......我知道......我正在寻找一种更优雅的方式,它已经将 de 元素分配给特定变量 xab。也许还允许我做case let [a, _, c, ...]
  • 我看不出它有什么好处。数组有随机访问的下标
  • 优点是可读性。我正在向下一位开发人员传达我将 array[0] 称为 x 的信息(当然,在这种情况下并不意味着什么,但这只是一个示例)。通过查看 Haskell,您绝对可以找到更好的示例,它广泛使用了这种类型的模式匹配。我正在寻找类似的东西。也类似于 JavaScript 的解构。显然问题中的例子不是我寻找这个的唯一原因=),我想做的还有很多。
  • 不愿意做显而易见的事情并打开array.count只是顽固的愚蠢。

标签: arrays swift


【解决方案1】:

你可以这样写,但如果你有多个元素,它很快就会变得笨拙:

switch array.count
{
   case 0 : print("No elements")

   case 1 : let x = array[0]
            print(x)

   case 2 : let (a,b) = (array[0],array[1])
            print(a)
            print(b)

   default: print("More than 2 elements")
}

【讨论】:

    【解决方案2】:

    为了实现这样的目标,我编写了一些最愚蠢的代码。

    for a in [ [], [0], [0,1], [0,1,2], [0,1,2,3,4], [0,1,2,3,4,5,6] ] {
        Switch(a) {
            Case { x in
                print("Singleton: \(x)")
            }
            Case { (x, y) in
                print("Pair: \(x), \(y)")
            }
            Case { (x, y, z) in
                print("Triple: \(x), \(y), \(z)")
            }
            Case(a.count == 4 || a.count == 5) {
                print("I didn't feel like splitting these up: \(a)")
            }
            Case(a.count >= 6) { () -> ControlFlow in // Type checker is too slow to leave the type out
                print("It's a big one: \(a)")
                return .fallthrough
            }
            Default {
                print("This falls out of range of the special overloads: \(a)")
            }
        }
    }
    
    // Implementation below
    func Switch<T>(_ v: T, @SwitchBuilder<T> _ cases: () -> [Case<T>]) {
        var fallingThrough = false
        for c in cases() {
            if fallingThrough || c.match(v) {
                switch c.run(v) {
                case .break:
                    return
                case .fallthrough:
                    fallingThrough = true
                }
            }
        }
    }
    @resultBuilder
    enum SwitchBuilder<T> {
        static func buildExpression(_ d: Default<T>) -> Case<T> {
            Case(d)
        }
        static func buildExpression(_ c: Case<T>) -> Case<T> {
            c
        }
        static func buildBlock(_ cs: Case<T>...) -> [Case<T>] {
            cs
        }
    }
    enum ControlFlow {
        case `break`
        // Be careful with this one, it's probably a bad idea.
        case `fallthrough`
    }
    struct Case<T> {
        var match: (T) -> Bool
        var run: (T) -> ControlFlow
        
        init(_ condition: @autoclosure @escaping () -> Bool, f: @escaping () -> ControlFlow) {
            self.match = { _ in condition() }
            self.run = { _ in f() }
        }
        init(_ condition: @autoclosure @escaping () -> Bool, f: @escaping () -> ()) {
            self.match = { _ in condition() }
            self.run = { _ in f(); return .break }
        }
        init<E>(_ f: @escaping (E) -> ControlFlow) where T == [E] {
            self.match = { $0.count == 1 }
            self.run = { f($0[0]) }
        }
        init<E>(_ f: @escaping (E) -> ()) where T == [E] {
            self.match = { $0.count == 1 }
            self.run = { f($0[0]); return .break }
        }
        init<E>(_ f: @escaping (E,E) -> ControlFlow) where T == [E] {
            self.match = { $0.count == 2 }
            self.run = { f($0[0], $0[1]) }
        }
        init<E>(_ f: @escaping (E,E) -> ()) where T == [E] {
            self.match = { $0.count == 2 }
            self.run = { f($0[0], $0[1]); return .break }
        }
        init<E>(_ f: @escaping (E,E,E) -> ControlFlow) where T == [E] {
            self.match = { $0.count == 3 }
            self.run = { f($0[0], $0[1], $0[2]) }
        }
        init<E>(_ f: @escaping (E,E,E) -> ()) where T == [E] {
            self.match = { $0.count == 3 }
            self.run = { f($0[0], $0[1], $0[2]); return .break }
        }
        init<E>(_ f: @escaping (E,E,E,E) -> ControlFlow) where T == [E] {
            self.match = { $0.count == 4 }
            self.run = { f($0[0], $0[1], $0[2], $0[3]) }
        }
        init<E>(_ f: @escaping (E,E,E,E) -> ()) where T == [E] {
            self.match = { $0.count == 4 }
            self.run = { f($0[0], $0[1], $0[2], $0[3]); return .break }
        }
        init<E>(_ f: @escaping (E,E,E,E,E) -> ControlFlow) where T == [E] {
            self.match = { $0.count == 5 }
            self.run = { f($0[0], $0[1], $0[2], $0[3], $0[4]) }
        }
        init<E>(_ f: @escaping (E,E,E,E,E) -> ()) where T == [E] {
            self.match = { $0.count == 5 }
            self.run = { f($0[0], $0[1], $0[2], $0[3], $0[4]); return .break }
        }
        init(_ d: Default<T>) {
            self.match = { _ in true }
            self.run = d.run
        }
    }
    struct Default<T> {
        var run: (T) -> ControlFlow
        
        init(_ f: @escaping () -> ControlFlow) {
            self.run = { _ in f() }
        }
        init(_ f: @escaping () -> ()) {
            self.run = { _ in f(); return .break }
        }
    }
    

    如您所料,这是打印出来的

    This falls out of range of the special overloads: []
    Singleton: 0
    Pair: 0, 1
    Triple: 0, 1, 2
    I didn't feel like splitting these up: [0, 1, 2, 3, 4]
    It's a big one: [0, 1, 2, 3, 4, 5, 6]
    This falls out of range of the special overloads: [0, 1, 2, 3, 4, 5, 6]
    

    这个实现只允许你解构长度为 5 的数组,但是通过向 Case 添加额外的初始化器来添加更多的情况是微不足道的。 除了如何使用它之外,Swift 不知道这意味着什么,因此您将无法使用它从您的函数中返回(returnCase 就像 break 内普通 case ) 或初始化变量,并且没有强制执行默认值。

    请不要在实际项目中使用它。或者,我只是一个在互联网上的人,你不必听我的。

    【讨论】:

      猜你喜欢
      • 2021-04-08
      • 2013-12-15
      • 2011-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多