Ответ Тирана Ут (и комментарии) мне очень помог, и я пометил его как правильный.Тем не менее, я подумал, что стоит добавить еще один ответ, чтобы поделиться некоторыми вещами, которые я узнал, и представить другой способ решения проблемы.
Документация Apple hash(into:)
гласит:
Компоненты, используемые для хеширования, должны совпадать с компонентами, сравниваемыми в реализации оператора == вашего типа.
Это все хорошо, если это просто однозначноодно сравнение свойств (как показывают все примеры кода!), но что если ваш метод ==
имеет условную логику, подобную моей?Как вы переводите это в значение (или значения) для подачи хеша?
Я зацикливался на этой детали, пока Тиран не предположил, что подача на хеш постоянное значение (например, 2) все равно будет работать,поскольку коллизии хешей разрешаются ==
в любом случае.Конечно, вы бы этого не делали в процессе производства, потому что потеряли бы все преимущества производительности при поиске хэшей, но для меня главное, что если вы не можете привести аргументы хэширования точно ,так же, как ваши ==
операнды, сделайте логику равенства хеш-функций больше включительно (не меньше).
Решения в ответе Тиран Ут работают, потому что побитовые операции не заботятся о том, в каком порядкеоперанды включены, как моя ==
логика.Иногда две совершенно разные пары могут генерировать одно и то же значение (что приводит к гарантированному конфликту хэшей), но единственным реальным следствием в этих случаях является небольшой удар по производительности.
В конце концов, я понял, что я может использовать одну и ту же логику в обоих случаях, тем самым избегая коллизий хешей - ну, в любом случае, кроме любых, вызванных несовершенным алгоритмом хеширования.Я добавил новую закрытую константу в MultiplicationQuestion
и инициализировал ее следующим образом:
uniqueOperands = Set([leftOperand, rightOperand])
Сортированный массив тоже бы сработал, но Set казался более элегантным выбором.Поскольку в Set нет порядка, моя подробная условная логика для ==
(с использованием &&
и ||
) уже аккуратно заключена в тип Set
.
Теперь я могу просто использовать оченьТо же значение для проверки на равенство и для подачи хеша:
static func ==(lhs: MultiplicationQuestion, rhs: MultiplicationQuestion) -> Bool {
return lhs.uniqueOperands == rhs.uniqueOperands
}
func hash(into hasher: inout Hasher) {
hasher.combine(uniqueOperands)
}
Я проверил производительность, и она находится на уровне битовых операций.Не только это, но мой код стал более кратким и читаемым в процессе.Кажется беспроигрышным.