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

时间:2021-07-16 16:14:15

I've run into a situation that I'm sure is not that uncommon. I have two arrays of objects that conform to a protocol and I want to check if they are the equal.

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

What I'd really like to do is this:

我真正想做的是:

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

However, this leads to the compile error:

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

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

错误:(10,30)协议'Pattern'只能用作通用约束,因为它具有Self或相关类型要求

Based on this post I realise that I need to lose the Equatable inheritance on my protocol and push it down onto the concrete 'Pattern' declarations. Though I really don't understand why. If I'm defining how the two objects are equal based on the protocol by overloading == there really is no issue as far as I can see. I don't even need to know the actual types or whether they are classes or structs.

基于这篇文章,我意识到我需要在协议上丢失Equatable继承并将其推送到具体的“Pattern”声明。虽然我真的不明白为什么。如果我通过重载来定义基于协议的两个对象如何相等==就我所见,确实没有问题。我甚至不需要知道实际类型或它们是类还是结构。

Regardless, this is all well and good and I can now compare concretePattern.isEqualTo(otherConcretePattern) but the issue remains that I can no longer compare arrays of these objects like I can compare an array of a concrete type as array equality relies on overloading the == operator.

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

The best I've managed to do so far is glom an isEqualTo method onto CollectionType via an extension. This at least allows me to compare arrays. But frankly, this code stinks.

到目前为止,我设法做的最好的是通过扩展将一个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) }
    }
}

Is there really no other way of doing this? Please tell me I'm missing something obvious.

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

2 个解决方案

#1


5  

I have two arrays of objects that conform to a protocol and I want to check if they are the equal.

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

So you want to say the two arrays are equal if all the elements in them are equal and the elements all conform to pattern. i.e.

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

If a, b, c and d are all things that conform to Pattern, you want

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

The easiest way to do this is actually to define an equality operator for two arrays of patterns i.e.

最简单的方法是为两个模式阵列定义一个相等运算符,即

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
}

I defined two types that conform to Pattern and tried various equality compares and it all works

我定义了两种符合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

#2


1  

The Swift answer:

斯威夫特回答:

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

This is a pattern (pun intended) that I developed myself, and it works extremely well for situations like these. _Pattern is an auto-implemented protocol courtesy of the new protocol-extension feature, and represents a type-erased version of Pattern.

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

#1


5  

I have two arrays of objects that conform to a protocol and I want to check if they are the equal.

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

So you want to say the two arrays are equal if all the elements in them are equal and the elements all conform to pattern. i.e.

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

If a, b, c and d are all things that conform to Pattern, you want

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

The easiest way to do this is actually to define an equality operator for two arrays of patterns i.e.

最简单的方法是为两个模式阵列定义一个相等运算符,即

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
}

I defined two types that conform to Pattern and tried various equality compares and it all works

我定义了两种符合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

#2


1  

The Swift answer:

斯威夫特回答:

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

This is a pattern (pun intended) that I developed myself, and it works extremely well for situations like these. _Pattern is an auto-implemented protocol courtesy of the new protocol-extension feature, and represents a type-erased version of Pattern.

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