Maak aangepaste Swift-gelijkheid en hashing één onveranderlijke waardedefinitie gebruiken. Deze walkthrough bouwt een lokale tegelsleutel wiens naam hoofdlettergevoeligheid negeert terwijl de dimensies exact blijven, en controleert vervolgens gelijkheid, set-lidmaatschap en dictionary-opzoeking.
Definieer precies wanneer twee tegels hetzelfde zijn
Stel dat je een kleine Mac-desktopgrafische oefening bouwt. Je moet synthetische tegelbeschrijvingen groeperen op naam, breedte en hoogte. De taak van de lezer is om die groepering consistent te implementeren: Orbit en ORBIT met dezelfde dimensies moeten gelijke sleutels produceren; het veranderen van één van de dimensies moet een andere sleutel produceren.
Schrijf het contract voordat je een van de protocollen implementeert. In dit voorbeeld zijn twee sleutels gelijk wanneer hun case-gevouwen namen, breedtes en hoogtes gelijk zijn. Dimensies zijn positieve gehele teken-eenheden. Voorloop- en aanloopspaties blijven significant. Dit is een illustratieve toepassingsregel, geen vereiste opgelegd door Swift.
| Synthetische invoeren | Verwachte relatie | Reden |
|---|---|---|
Orbit, 40 × 20 en ORBIT, 40 × 20 |
Gelijk | Hoofdlettergevoeligheid wordt genegeerd |
Orbit, 40 × 20 en orbit, 41 × 20 |
Ongelijk | Breedte deelt mee in gelijkheid |
Orbit, 40 × 20 en orbit, 40 × 21 |
Ongelijk | Hoogte deelt mee in gelijkheid |
Orbit, 40 × 20 en orbit , 40 × 20 |
Ongelijk | De aanloopspatie wordt behouden |
Gebruik een speciale sleutel wanneer een bepaalde groeperingsregel afwijkt van de volledige waarde van een getoonde tegel. Behoud de oorspronkelijke weergavespelling in het omringende presentatiemodel. Dat voorkomt dat twee complete tegelbeschrijvingen uitwisselbaar worden verklaard terwijl zichtbaar verschillende namen deel uitmaken van hun waarde. Swifts Equatable-documentatie beschrijft gelijkheid in termen van substitueerbaarheid en vereist reflexiviteit, symmetrie en transitiviteit. Equatable
Geef gelijkheid en hashing dezelfde componenten
Swifts Hashable-protocol erft van Equatable. De essentiële vereiste is dat gelijke instanties dezelfde waarden in dezelfde volgorde in de hasher voeren. Hashbare waarden kunnen dienen als set-elementen en dictionary-sleutels. Hashable
Een frequente fout is het vergelijken van genormaliseerde namen in == maar hashen van de oorspronkelijke spelling. De namen hebben dan verschillende betekenissen in de twee methoden. Een andere fout is het weglaten van breedte uit gelijkheid terwijl het wel in hashing wordt meegenomen. Beide ontwerpen staan toe dat waarden die gelijk worden verklaard, verschillende hashing-invoeren leveren. Apple geeft expliciet de richting om bij aangepaste implementaties dezelfde significante eigenschappen in beide methoden te gebruiken. Algemene protocollen adopteren
Voor dit voorbeeld plaats je de gelijkheidscomponenten in een privé geneste structuur. Laat Swift die structuur's gelijkheid en hashing synthetiseren, en delegeren beide outer operaties naar het. Dit maakt de componentenlijst een enkele declaratie die je kunt inspecteren. Swift ondersteunt gesynthetiseerde conformance voor structuren wiens opgeslagen eigenschappen voldoen aan de vereiste protocollen. Protocollen — Een protocol adopteren met een gesynthetiseerde implementatie
Als je type al precies zijn gelijkheidscomponenten opslaat, is gesynthetiseerde Hashable-conformance meestal voldoende. De expliciete outer methoden hieronder tonen hoe je een bestaande aangepaste implementatie aligned kunt houden zonder twee onafhankelijke veldlijsten te onderhouden.
Voer het complete lokale voorbeeld uit op je Mac
Je hebt een geïnstalleerde Swift-toolchain met Foundation nodig. In Terminal, controleer het met swift --version, zoals aanbevolen door Swifts getting-started-documentatie. Als Swift niet beschikbaar is, volg de officiële macOS-installatiegids voordat je doorgaat. Aan de slag, Swift installeren — macOS
Het volgende is één compleet shell-commando dat het Swift-programma bevat. Plak het hele blok in Terminal. Het levert het programma via standaardinvoer; het voorbeeld voert alleen in-memory berekeningen uit en print resultaten. De Swift-code is geïnspecteerd, maar kon niet uitgevoerd worden in de onderzoeksomgeving omdat Swift niet beschikbaar was.
swift - <<'SWIFT'
import Foundation
struct TileKey: Hashable, Sendable {
private struct Components: Hashable, Sendable {
let foldedName: String
let width: Int
let height: Int
}
private let components: Components
init(name: String, width: Int, height: Int) {
precondition(width > 0 && height > 0,
"Tile dimensions must be positive")
let foldedName = name.folding(
options: [.caseInsensitive],
locale: Locale(identifier: "en_US_POSIX")
)
components = Components(
foldedName: foldedName,
width: width,
height: height
)
}
static func == (lhs: TileKey, rhs: TileKey) -> Bool {
lhs.components == rhs.components
}
func hash(into hasher: inout Hasher) {
hasher.combine(components)
}
}
let a = TileKey(name: "Orbit", width: 40, height: 20)
let b = TileKey(name: "ORBIT", width: 40, height: 20)
let c = TileKey(name: "orbit", width: 40, height: 20)
let wider = TileKey(name: "orbit", width: 41, height: 20)
let taller = TileKey(name: "orbit", width: 40, height: 21)
let spaced = TileKey(name: "orbit ", width: 40, height: 20)
let samples = [a, b, c, wider, taller, spaced]
precondition(a == b && b == c && a == c)
precondition(a != wider && a != taller && a != spaced)
// Check the equality laws over this finite sample.
for first in samples {
precondition(first == first, "Reflexivity failed")
for second in samples {
precondition((first == second) == (second == first),
"Symmetry failed")
if first == second {
precondition(first.hashValue == second.hashValue,
"Equal values have different hashes")
}
for third in samples where first == second && second == third {
precondition(first == third, "Transitivity failed")
}
}
}
let uniqueKeys = Set(samples)
precondition(uniqueKeys.count == 4)
precondition(uniqueKeys.contains(b))
let patterns = [a: "striped"]
precondition(patterns[b] == "striped")
print("Unique keys:", uniqueKeys.count)
print("Equivalent key found:", uniqueKeys.contains(b))
print("Pattern:", patterns[b] ?? "missing")
print("Sample contract checks passed")
SWIFT
Verwachte output:
Unieke sleutels: 4
Equivalente sleutel gevonden: true
Patroon: gestreept
Voorbeeldcontractcontroles geslaagd
De zes invoeren vormen vier groepen: de drie case-varianten delen één sleutel, terwijl de gewijzigde breedte, gewijzigde hoogte en aanloopspatie-variant elk een andere vormen. De dictionary-controle vraagt of een waarde geassocieerd met a kan worden opgehaald met de gelijke sleutel b. Deze controles oefenen het beoogde verzamelgedrag uit zonder afhankelijk te zijn van een bepaalde numerieke hash.
De initialiseerder stopt bewust op niet-positieve dimensies. Dat is geschikt voor deze vaste synthetische oefening, waar ongeldige dimensies een programmeerfout aangeven. Als een desktop-interface onvolledige dimensie-invoer toestaat, valideer die invoer voordat je de sleutel bouwt, of wijzig de initialiseerder om een optionele waarde te retourneren of een fout te gooien. Vervang niet stilzwijggend een dimensie, want dat verandert het gelijkheidscontract.
Maak tekstnormalisatie een expliciete regel
De normalisatiestap roept Foundation's folding(options:locale:) aan via Swifts string-API. De API retourneert een string met de opge vouwopties toegepast. Hier wordt alleen .caseInsensitive aangevraagd. StringProtocol.folding(options:locale:)
Foundation documenteert case-, breedte- en diakritiekvouwing als afzonderlijke keuzes. Het legt ook uit dat de locale vouwresultaten kan veranderen en dat gevouwen tekst ongeschikt kan zijn voor weergave. Het voorbeeld levert daarom een vaste locale en bewaart het gevouwen resultaat alleen binnen de sleutel. De synthetische namen zijn bewust simpel. Deze locale-keuze definieert het gedrag van deze oefening; het is geen universele aanbeveling voor elke naamgevinginterface. NSString.folding(options:locale:)
Het nuttige ontwerpbesluit is om eenmaal te normaliseren tijdens constructie. Gelijkheid en hashing gebruiken daarna het opgeslagen resultaat, zodat geen van de operaties onafhankelijk een locale of transformatie kiest. Als de applicatie later een ander vergelijkingsbeleid nodig heeft, bouw sleutels onder dat nieuwe beleid en herbouw de betrokken in-memory verzameling.
Voordat je normalisatie verbreekt, voeg voorbeelden toe die de gewenste groepen onderscheiden. Mogen spaties uitmaken? Moeten geaccentueerde letters distinct blijven? Moeten visueel verschillende tekendekten tellen als dezelfde naam? Beantwoord die vragen als productregels, en implementeer dan één transformatie die door beide operaties wordt gebruikt.
Diagnosticeren van een mismatch zonder te vertrouwen op hash-nummers
Gebruik de volgende volgorde bij het aanpassen van dit ontwerp aan een bestaand type:
- Schrijf één paar dat gelijk moet zijn en één paar voor elk veld dat waarden moet onderscheiden.
- Traceer de daadwerkelijke waarden die worden vergeleken door
==, inclusief normalisatie en afgeleide eigenschappen. - Traceer de waarden die worden geleverd aan
hasher.combine, inclusief hun volgorde. - Verplaats de gedeelde definitie naar één onveranderlijke componentwaarde, of zorg dat beide implementaties expliciet dezelfde componenten gebruiken.
- Controleer gelijkheidswetten, gelijke-waarde-hashes, set-lidmaatschap en dictionary-opzoeking met dezelfde fixtures.
Implementeer geen gelijkheid door hashValue te vergelijken. Gelijke waarden moeten gelijke hashes hebben, maar een matchende hash stelt geen gelijkheid vast. De volledige gelijkheidscomponenten blijven de basis voor ==. Evenzo eist een test die vereist dat elk ongelijk paar verschillende hashes heeft, meer dan het hashing-contract garandeert. Hashable
Vergelijk hashes alleen binnen de huidige uitvoering bij het controleren van dit contract. Swifts Hasher gebruikt willekeurige seeding, en het algoritme kan veranderen tussen standaardbibliotheekversies. Een geprint integer van gisteren is daarom ongeschikt als verwacht resultaat voor vandaag. Hasher
De eindige steekproefcontroles zijn nuttige regressiecontroles, geen bewijs voor elke mogelijke string en dimensie. Het structurele argument telt ook: beide outer methoden delegeren naar dezelfde onveranderlijke Components-waarde, wiens opgeslagen velden het gekozen contract volledig uitdrukken.
Houd concurrency-vereisten gescheiden van gelijkheid
Het voorbeeld verklaart beide structuren Sendable omdat hun opgeslagen waarden strings, integers en een andere sendable structuur zijn. Swift documenteert dat sendable structuren sendable opgeslagen eigenschappen moeten bevatten. Sendability betreft het overdragen van waarden tussen concurrency-domeinen; het definieert niet welke waarden gelijk zijn. Sendable, The Swift Programming Language — Concurrency
Deze lokale berekening heeft geen taken of actors nodig. Als het later deel uitmaakt van concurrente desktop-code, houd gelijkheid en hashing dan als synchrone operaties op de opgeslagen componenten van de sleutel. Behandel elke gedeelde veranderlijke verzameling als een apart concurrency-ontwerpvraagstuk. De taalhandleiding beschrijft actors en isolatie als mechanismen voor het coördineren van toegang tot veranderlijke state. The Swift Programming Language — Concurrency
Er is ook een Foundation-grens bij het aanpassen van het patroon: een NSObject-subklasse gebruikt geërfde gelijkheids- en hashing-gedrag. Apples ondersteunde aanpassingspad is om isEqual(_:) en hash samen te overschrijven. De platte Swift-structuur in dit voorbeeld gebruikt == en hash(into:). Algemene protocollen adopteren
Voor een lokale tegelsleutel is het onderhoudbare resultaat een geschreven vergelijkingsregel, één onveranderlijke vertegenwoordiging van die regel, en controles die daadwerkelijke verzamelopzoeking oefenen. Wanneer de definitie verandert, werk de componentvertegenwoordiging en zijn voorbeelden samen bij, zodat gelijkheid en hashing dezelfde waarde blijven beschrijven.
Bronnen
- Hashable — Apple
- Equatable — Apple
- Hasher — Apple
- Algemene protocollen adopteren — Apple
- folding(options:locale:) — StringProtocol — Apple
- folding(options:locale:) — NSString — Apple
- Sendable — Apple
- Protocollen — The Swift Programming Language, Swift.org
- Concurrency — The Swift Programming Language, Swift.org
- Aan de slag — Swift.org
- Swift installeren — macOS — Swift.org