Быстрый набор типов структур
Скажем, у меня есть 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 .....
}