如何在Swift中比较两个协议数组是否相等?

时间:2015-10-15 11:15:46

标签: arrays swift protocols swift2 heterogeneous

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

我真正想做的是:

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

真的没有别的办法吗?请告诉我,我错过了一些明显的东西。

2 个答案:

答案 0 :(得分:6)

  

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

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

如果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

答案 1 :(得分:1)

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的类型删除版本。