0Pricing
Swift Academy · Урок

Пользовательская семантика равенства

Реализуйте правила равенства, соответствующие предметной области.

«Пользовательская семантика равенства» — бесплатный урок Swift Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Swift Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Swift Academy содержит 4 уроков всего.

Когда стандартный == даёт неверный результат

Синтезированный == сравнивает каждое хранимое свойство. Иногда это слишком строго (Вас интересует только идентификатор) или, наоборот, слишком нестрого. В таком случае напишите == самостоятельно.

Равенство по идентификатору

Считайте две записи равными, если их идентификаторы совпадают, игнорируя остальные поля:

struct Account: Equatable {
    let id: Int
    var balance: Int
    static func == (l: Account, r: Account) -> Bool { l.id == r.id }
}
print(Account(id: 1, balance: 10) == Account(id: 1, balance: 999))  // true

Равенство без учёта регистра

Перед сравнением приведите значения к нормализованному виду:

struct Username: Equatable {
    let raw: String
    static func == (l: Username, r: Username) -> Bool {
        l.raw.lowercased() == r.raw.lowercased()
    }
}
print(Username(raw: "Ann") == Username(raw: "ANN"))  // true

Согласованность хешируемости

Если Вы настраиваете ==, MUST обновить hash(into:), чтобы он использовал те же поля, иначе равные значения могут получить разные хеши и нарушить работу Set.

Согласование == и hash(into:)

Оба механизма используют только id, поэтому остаются согласованными:

struct User: Hashable {
    let id: Int
    var name: String
    static func == (l: User, r: User) -> Bool { l.id == r.id }
    func hash(into h: inout Hasher) { h.combine(id) }
}
print(Set([User(id: 1, name: "A"), User(id: 1, name: "B")]).count)  // 1

Равенство без учёта кэша

Исключите производные или кэшированные поля из сравнения:

struct Doc: Equatable {
    let text: String
    var cachedWordCount: Int
    static func == (l: Doc, r: Doc) -> Bool { l.text == r.text }
}
let a = Doc(text: "hi", cachedWordCount: 1)
let b = Doc(text: "hi", cachedWordCount: 999)
print(a == b)  // true

Равенство чисел с плавающей точкой с допуском

Сравнивайте числа double с заданной эпсилон-точностью, а не на точное совпадение:

struct Measure: Equatable {
    let value: Double
    static func == (l: Measure, r: Measure) -> Bool {
        abs(l.value - r.value) < 0.0001
    }
}
print(Measure(value: 0.1 + 0.2) == Measure(value: 0.3))  // true

Равенство по подмножеству полей

Для предметного равенства сравнивайте значимое подмножество полей:

struct Card: Equatable {
    let rank: Int
    let suit: String
    let isFaceUp: Bool   // display state, not identity
    static func == (l: Card, r: Card) -> Bool {
        l.rank == r.rank && l.suit == r.suit
    }
}
print(Card(rank: 1, suit: "H", isFaceUp: true) == Card(rank: 1, suit: "H", isFaceUp: false))  // true

Соблюдайте законы Equatable

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

Симметричная реализация

Сравнение нормализованных форм сохраняет симметрию:

struct Email: Equatable {
    let address: String
    static func == (l: Email, r: Email) -> Bool {
        l.address.lowercased() == r.address.lowercased()
    }
}
let x = Email(address: "A@x.com")
let y = Email(address: "a@X.com")
print(x == y, y == x)  // true true

Пользовательское равенство в множестве

Поскольку равенство и хеширование только по идентификатору согласованы, Set считает значения с одним идентификатором одним элементом:

struct Item: Hashable {
    let id: Int
    var note: String
    static func == (l: Item, r: Item) -> Bool { l.id == r.id }
    func hash(into h: inout Hasher) { h.combine(id) }
}
let s: Set<Item> = [Item(id: 1, note: "x"), Item(id: 1, note: "y")]
print(s.count)  // 1

Быстрая проверка

Что ещё нужно сделать после написания пользовательского ==, использующего только некоторые поля?

Итоги

Вы изучили пользовательское равенство:

  • Пишите собственный ==, когда стандартное сравнение всей структуры даёт неверный результат
  • Распространённые случаи: только идентификатор, сравнение без учёта регистра, числа с плавающей точкой с допуском, игнорирование кэша
  • Согласуйте hash(into:) с теми же полями
  • Сохраняйте рефлексивность, симметричность и транзитивность

Курс завершён! Далее: CustomStringConvertible и Mirror.

Часто задаваемые вопросы

Урок «Пользовательская семантика равенства» бесплатный?

Да — полный текст урока «Пользовательская семантика равенства» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Swift Academy, подпишись на CoddyKit PRO. Курс Swift Academy содержит 4 уроков всего.

Чему я научусь в уроке «Пользовательская семантика равенства»?

Реализуйте правила равенства, соответствующие предметной области. Ты практикуешь Swift Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Swift Academy?

Предыдущий опыт не требуется. Swift Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Пользовательская семантика равенства»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Swift Academy?

Да. Каждый урок Swift Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Синтезированный Equatable
  2. Hashable и hash(into:)
  3. Comparable и сортировка
  4. Пользовательская семантика равенства
← Назад к Swift Academy