Как сравнить два массива протоколов на равенство в Swift?



Я столкнулся с ситуацией, которая, я уверен, не является такой уж редкостью. У меня есть два массива объектов, которые соответствуют протоколу, и я хочу проверить, равны ли они.



Что я действительно хотел бы сделать, так это:



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



Основываясь наэтом посте , я понимаю, что мне нужно потерять эквивалентное наследование в моем протоколе и спихнуть его на конкретные объявления "шаблона". Хотя я действительно не понимаю почему?. Если я определяю, как два объекта равны, основываясь на протоколе, перегружая ==, насколько я могу видеть, на самом деле нет никакой проблемы. Мне даже не нужно знать фактические типы или являются ли они классами или структурами.



Несмотря на это, все это хорошо, и теперь я могу сравнить concretePattern.isEqualTo(otherConcretePattern), но проблема остается в том, что я больше не могу сравнивать массивы этих объектов, как я могу сравнить массив конкретного типа, поскольку равенство массивов зависит от перегрузки оператора==.



Лучший Мне удалось сделать это до сих пор-это glom метод 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) }
}
}


Неужели нет другого способа сделать это? Пожалуйста, скажи мне, что я упускаю что-то очевидное.
668   2  

2 ответов:

У меня есть два массива объектов, которые соответствуют протоколу, и я хочу проверить, равны ли они.
Таким образом, вы хотите сказать, что два массива равны, если все элементы в них равны и все элементы соответствуют шаблону. то есть

Если a, b, c и d-все вещи, которые соответствуют шаблону, вы хотите

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
}

Я определил два типа, которые соответствовать шаблону и пробовал различные равенства сравнивает и все это работает

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

Ответ 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.

Comments

    Ничего не найдено.