Быстрый набор типов структур

Скажем, у меня есть struct, который может быть любым:

struct Cube {
    var x: Int
    var y: Int
    var z: Int
    var width: Int
    // ...
}

Как мне создать Set этих точек, чтобы не было двух объектов с одинаковыми свойствами?

let points: Set<Cube> = Set()
// Type ‘Cube’ does not conform to protocol ‘Hashable’

Но не сразу понятно, как реализовать хеширование. Из того, что я читал, мне нужно сделать хэш-функцию, но это не выглядит легко возможным с количеством свойств, которые у меня есть в структуре.

Ответ 1

Прежде всего, Hashable extends Equatable, поэтому вы должны реализовать a ==, который сравнивает два значения, используя все свойства которые однозначно идентифицируют куб:

func ==(lhs: Cube, rhs: Cube) -> Bool {
    return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z && lhs.width == rhs.width
}

Протокол Hashable требует только

x == y означает x.hashValue == y.hashValue

так

var hashValue: Int {
    return 0
}

будет действительной (и рабочей) реализацией. Однако это поместите все объекты в одно и то же хэш-ведро набора (или словаря), что неэффективно. Лучшей реализацией является, например,

struct Cube: Hashable {
    var x: Int
    var y: Int
    var z: Int
    var width: Int

    var hashValue: Int {
        return x.hashValue ^ y.hashValue ^ z.hashValue ^ width.hashValue
    }
}

Здесь оператор "XOR" ^ выбран, потому что он не может переполняться. Вы также можете использовать "оператор переполнения" &+.

Более сложные хэш-функции могли бы различать разные значения лучше, так что заданные операции становятся быстрее. С другой стороны, вычисление самой хэш-функции будет медленнее. Поэтому я бы искал "лучшую" хэш-функцию только если заданные операции оказываются узким местом производительности в вашей программе.

Ответ 2

Реализация протокола Hashable состоит из двух вещей. Сначала реализует hashValue, а второй выполняет оператор равенства.

Для работы Hashable важной частью протокола является оператор равенства. Он должен быть реализован таким образом, который возвращает true, и только тогда, если две структуры содержат одинаковые значения.

С другой стороны, ваша реализация hashValue может возвращать буквально все, пока одна и та же структура всегда будет возвращать одинаковое значение.

Единственное, что влияет на hashValue, - это то, как быстро будет работать ваш код, потому что когда вы добавляете или просматриваете значения, первый код, который будет запускаться, hashValue. Если hashValue возвращает одно и то же значение для двух структур, то равенство между ними будет определяться вызовом оператора равенства, который в противном случае был бы пропущен.

struct Cube: Hashable {

    // satisfy Hashable requirement
    var hashValue: Int {
        get {
            // you can return any integer here
            return x &+ y &+ z &+...
            // or even the same one for all structs
            return 0
        }
    }
}

// satisfy Equatable requirement
func ==(lhs: Cube, rhs: Cube) -> Bool {
    return lhs.x == rhs.x && lhs.y == rhs.y .....
}