【问题标题】:How to compare two arrays of protocols for equality in Swift?如何在 Swift 中比较两个协议数组是否相等?
【发布时间】:2015-10-15 11:15:45
【问题描述】:

我遇到过一种情况,我确信这种情况并不少见。我有两个符合协议的对象数组,我想检查它们是否相等。

我真正想做的是:

protocol Pattern: Equatable
{
    func isEqualTo(other: Pattern) -> Bool
}

func ==(rhs:Pattern, lhs:Pattern) -> Bool
{
    return rhs.isEqualTo(lhs)
}

extension Equatable where Self : Pattern
{
    func isEqualTo(other: Pattern) -> Bool
    {
        guard let o = other as? Self else { return false }
        return self == o
    }
}

但是,这会导致编译错误:

Error:(10, 30) protocol 'Pattern' can only be used as a generic constraint because it has Self or associated type requirements

基于this post,我意识到我需要丢失协议上的 Equatable 继承,并将其下推到具体的“模式”声明中。虽然我真的不明白为什么。如果我通过重载 == 根据协议定义这两个对象如何相等,那么据我所知,这确实没有问题。我什至不需要知道实际的类型或者它们是类还是结构。

无论如何,这一切都很好,我现在可以比较concretePattern.isEqualTo(otherConcretePattern),但问题仍然是我不能再比较这些对象的数组,因为我可以比较具体类型的数组,因为数组相等依赖于重载== 运算符。

到目前为止,我所做的最好的事情是通过扩展将 isEqualTo 方法添加到 CollectionType 上。这至少允许我比较数组。但坦率地说,这段代码很臭。

extension CollectionType where Generator.Element == Pattern
{
    func isEqualTo(patterns:[Pattern]) -> Bool {
        return self.count as? Int == patterns.count && !zip(self, patterns).contains { !$0.isEqualTo($1) }
    }
}

真的没有其他方法可以做到这一点吗?请告诉我我遗漏了一些明显的东西。

【问题讨论】:

    标签: arrays swift protocols swift2 heterogeneous


    【解决方案1】:

    我有两个符合协议的对象数组,我想检查它们是否相等。

    所以你想说这两个数组是相等的,如果它们中的所有元素都相等并且元素都符合模式。即

    如果a、b、c、d都是符合Pattern的东西,你要

    a == c 
    a != b
    a != d
    b != d
    
    let array1: [Pattern] = [a, b, c]
    let array2: [Pattern] = [a, b, a]
    let array3: [Pattern] = [a, d, c]
    
    array1 == array2  // true
    array1 == array3  // false
    

    最简单的方法实际上是为两个模式数组定义一个相等运算符,即

    protocol Pattern
    {
        func isEqualTo(other: Pattern) -> Bool
    }
    
    func ==(rhs: Pattern, lhs: Pattern) -> Bool
    {
        return rhs.isEqualTo(lhs)
    }
    
    func ==(lhs: [Pattern], rhs: [Pattern]) -> Bool
    {
        guard lhs.count == rhs.count else { return false }
        var i1 = lhs.generate()
        var i2 = rhs.generate()
        var isEqual = true
        while let e1 = i1.next(), e2 = i2.next() where isEqual
        {
            isEqual = e1 == e2
        }
        return isEqual
    }
    

    我定义了两种符合 Pattern 的类型,并尝试了各种相等比较,一切正常

    struct Foo: Pattern
    {
        let data: String
        init(data: String)
        {
            self.data = data
        }
        func isEqualTo(other: Pattern) -> Bool
        {
            guard let other = other as? Foo else { return false }
            return self.data == other.data
        }
    }
    
    struct Bar: Pattern
    {
        let data: String
        init(data: String)
        {
            self.data = data
        }
        func isEqualTo(other: Pattern) -> Bool
        {
            guard let other = other as? Bar else { return false }
            return self.data == other.data
        }
    }
    
    let a = Foo(data: "jeremyp")
    let b = Bar(data: "jeremyp")
    let c = Foo(data: "jeremyp")
    let d = Foo(data: "jeremy")
    
    let comp1 = a == c // true
    let comp2 = a == b // false
    let comp3 = a == d // false
    
    let array1: [Pattern] = [a, b, c]
    let array2: [Pattern] = [a, b, a]
    let array3: [Pattern] = [a, d, c]
    
    let comp4 = array1 == array2 // true
    let comp5 = array1 == array3 // false
    

    【讨论】:

    • 所以答案就是不要让 Pattern 协议继承自 Equatable?
    • 我尝试了很多方法,结果证明这是最简单的。如果Pattern 不是Equatable,则无法比较Pattern 的数组,因此您必须为数组创建自己的相等运算符。
    • 在尝试从 Equatable 继承之前,我实际上曾经做过一次。我不知道为什么我忘记了。感谢您的帮助。
    • Swift 警告我 == 数组函数。 Member operator '==' of protocol must have at least one argument of type Self。有什么想法吗?
    【解决方案2】:

    Swift 答案:

    protocol _Pattern
    {
        func _isEqualTo(_other: Any) -> Bool?
    }
    
    extension _Pattern where Self: Pattern
    {
        func _isEqualTo(_other: Any) -> Bool?
        {
            return (_other as? Self).map({ self.isEqualTo($0) })
        }
    }
    
    protocol Pattern: _Pattern, Equatable
    {
        func isEqualTo(other: Self) -> Bool
    }
    
    extension Pattern
    {
        func isEqualTo(other: _Pattern) -> Bool
        {
            return _isEqualTo(other) ?? false
        }
    }
    
    func == <T: Pattern>(rhs: T, lhs: T) -> Bool
    {
        return rhs.isEqualTo(lhs)
    }
    

    这是我自己开发的模式(双关语),它非常适合此类情况。 _Pattern 是由新协议扩展功能提供的自动实现协议,代表Pattern 的类型擦除版本。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-20
      • 1970-01-01
      • 1970-01-01
      • 2013-08-18
      • 2021-05-23
      • 1970-01-01
      相关资源
      最近更新 更多