Özel Swift eşitliği ve hashlemesini tek bir değişmez değer tanımına dayandırın. Bu anlatımda, adı harf büyüklüğünü göz ardı eden ancak boyutları tam olarak dikkate alan yerel bir döşeme anahtarı oluşturuluyor; ardından eşitlik, küme üyeliği ve sözlük araması denetleniyor.
İki döşemenin ne zaman aynı olduğunu kesin olarak tanımlayın
Küçük bir Mac masaüstü grafik alıştırması yaptığınızı varsayalım. Sentetik döşeme açıklamalarını ada, genişliğe ve yüksekliğe göre gruplamanız gerekiyor. Okuyucunun görevi, bu gruplamayı tutarlı biçimde uygulamak: Aynı boyutlardaki Orbit ve ORBIT eşit anahtarlar üretmeli; boyutlardan herhangi birini değiştirmek farklı bir anahtar üretmeli.
Her iki protokolü uygulamadan önce kuralları yazın. Bu örnekte iki anahtar; harf büyüklüğünden arındırılmış adları, genişlikleri ve yükseklikleri eşitse eşittir. Boyutlar pozitif tam sayı çizim birimleridir. Baştaki ve sondaki boşluklar anlamlıdır. Bu, Swift'in koyduğu bir gereklilik değil, uygulamaya özgü örnek bir kuraldır.
| Sentetik girdiler | Beklenen ilişki | Gerekçe |
|---|---|---|
Orbit, 40 × 20 ve ORBIT, 40 × 20 |
Eşit | Harf büyüklüğü göz ardı edilir |
Orbit, 40 × 20 ve orbit, 41 × 20 |
Eşit değil | Genişlik eşitliğe dahildir |
Orbit, 40 × 20 ve orbit, 40 × 21 |
Eşit değil | Yükseklik eşitliğe dahildir |
Orbit, 40 × 20 ve orbit , 40 × 20 |
Eşit değil | Sondaki boşluk korunur |
Belirli bir gruplama kuralı, görüntülenen bir döşemenin tam değerinden farklı olduğunda özel bir anahtar kullanın. Özgün görüntüleme yazımını çevresindeki sunum modelinde koruyun. Böylece değerlerinin parçası olarak adları görünür biçimde farklı olan iki tam döşeme açıklamasının birbirinin yerine kullanılabilir olduğunu ilan etmemiş olursunuz. Swift’in Equatable belgeleri eşitliği, birbirinin yerine kullanılabilirlik üzerinden açıklar ve yansıma, simetri ve geçişlilik özelliklerini gerektirir. Equatable
Eşitlik ve hashlemede aynı bileşenleri kullanın
Swift’in Hashable protokolü Equatable protokolünden türer. Temel gereklilik, eşit örneklerin aynı değerleri aynı sırayla hashleyiciye aktarmasıdır. Hashable değerler, küme öğeleri ve sözlük anahtarları olarak kullanılabilir. Hashable
Sık yapılan bir hata, == içinde normalleştirilmiş adları karşılaştırıp özgün yazımı hashlemektir. Böylece iki yöntem adlara farklı anlamlar yükler. Bir başka hata, eşitlikte genişliği dışarıda bırakıp hashlemede kullanmaktır. Her iki tasarım da eşit olduğu belirtilen değerlerin farklı hashleme girdileri sağlamasına yol açar. Apple, özel uygulamalarda her iki yöntemde de aynı anlamlı özelliklerin kullanılmasını açıkça önerir. Yaygın Protokolleri Benimseme
Bu örnekte eşitlik bileşenlerini iç içe, özel bir yapıya koyun. Swift'in bu yapının eşitlik ve hashlemesini sentezlemesine izin verin, ardından dıştaki her iki işlemi de bu yapıya yönlendirin. Böylece bileşen listesi inceleyebileceğiniz tek bir tanımda yer alır. Swift, saklanan özellikleri gerekli protokollere uyan yapılar için sentezlenmiş uyumluluğu destekler. Protokoller — Sentezlenmiş Bir Uygulamayla Protokolü Benimseme
Türünüz zaten tam olarak eşitlik bileşenlerini saklıyorsa, sentezlenmiş Hashable uyumluluğu genellikle tek başına yeterlidir. Aşağıdaki açık dış yöntemler, mevcut özel uygulamanın iki bağımsız alan listesini ayrı ayrı sürdürmeden nasıl uyumlu tutulacağını gösterir.
Mac'inizde yerel örneğin tamamını çalıştırın
Foundation içeren bir Swift araç zincirinin kurulu olması gerekir. Swift'in başlangıç belgelerinde önerildiği üzere, Terminal içinde swift --version komutuyla denetleyin. Swift kullanılamıyorsa devam etmeden önce resmi macOS kurulum kılavuzunu izleyin. Başlarken, Swift'i Yükleme — macOS
Aşağıdaki blok, Swift programını içeren tek bir tam kabuk komutudur. Bloğun tamamını Terminal'e yapıştırın. Programı standart girdi aracılığıyla sağlar; örnek yalnızca bellekte hesaplamalar yapar ve sonuçları yazdırır. Swift kodu incelendi, ancak Swift araştırma ortamında kullanılamadığından çalıştırılamadı.
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
Beklenen çıktı:
Benzersiz anahtar sayısı: 4
Eşdeğer anahtar bulundu: true
Desen: çizgili
Örnek kuralları denetimleri başarılı
Altı girdi dört grup oluşturur: Üç harf büyüklüğü çeşitlemesi aynı anahtarı paylaşırken, değiştirilmiş genişlik, değiştirilmiş yükseklik ve sondaki boşluk içeren çeşitlemenin her biri ayrı bir grup oluşturur. Sözlük denetimi, a ile ilişkilendirilmiş bir değerin eşit b anahtarı kullanılarak alınıp alınamayacağını sınar. Bu denetimler, belirli bir sayısal hash değerine bağlı kalmadan amaçlanan koleksiyon davranışını sınar.
Başlatıcı, sıfırdan büyük olmayan boyutlarda kasıtlı olarak işlemi durdurur. Geçersiz boyutların programlama hatasına işaret ettiği bu sabit sentetik alıştırma için bu uygundur. Masaüstü arayüzü eksik boyut girişine izin veriyorsa anahtarı oluşturmadan önce bu girdiyi doğrulayın ya da başlatıcıyı isteğe bağlı değer döndürecek veya hata fırlatacak biçimde değiştirin. Bir boyutu sessizce başka bir değerle değiştirmeyin; çünkü bu, eşitlik kuralını değiştirir.
Metin normalleştirmeyi açık bir kural haline getirin
Normalleştirme adımı, Swift'in dize API'si üzerinden Foundation'ın folding(options:locale:) yöntemini çağırır. API, belirtilen katlama seçenekleri uygulanmış bir dize döndürür. Burada yalnızca .caseInsensitive istenir. StringProtocol.folding(options:locale:)
Foundation; harf büyüklüğü, genişlik ve aksan işaretlerine göre katlamayı ayrı seçenekler olarak belgeler. Ayrıca yerel ayarın katlama sonuçlarını değiştirebileceğini ve katlanmış metnin görüntüleme için uygun olmayabileceğini açıklar. Bu nedenle örnek sabit bir yerel ayar sağlar ve katlanmış sonucu yalnızca anahtarın içinde saklar. Sentetik adlar özellikle basit seçilmiştir. Bu yerel ayar seçimi, bu alıştırmanın davranışını tanımlar; her adlandırma arayüzü için evrensel bir öneri değildir. NSString.folding(options:locale:)
Yararlı tasarım kararı, oluşturma sırasında yalnızca bir kez normalleştirmektir. Eşitlik ve hashleme sonrasında saklanan sonucu kullanır; böylece bu işlemlerin hiçbiri bağımsız olarak yerel ayar veya dönüşüm seçmez. Uygulamanın daha sonra farklı bir karşılaştırma ilkesine ihtiyacı olursa anahtarları bu yeni ilkeye göre oluşturun ve etkilenen bellek içi koleksiyonu yeniden kurun.
Normalleştirmeyi genişletmeden önce, istenen grupları birbirinden ayıran örnekler ekleyin. Boşluklar anlamlı olmalı mı? Aksanlı harfler ayrı kalmalı mı? Görsel olarak farklı karakter genişlikleri aynı ad sayılmalı mı? Bu soruları ürün kuralları olarak yanıtlayın, ardından her iki işlemde de kullanılan tek bir dönüşüm uygulayın.
Hash sayılarına güvenmeden uyumsuzluğu teşhis edin
Bu tasarımı mevcut bir türe uyarlarken şu sırayı izleyin:
- Eşit olması gereken bir çift ve değerleri birbirinden ayırması gereken her alan için bir çift yazın.
- Normalleştirme ve türetilmiş özellikler dahil olmak üzere
==tarafından karşılaştırılan gerçek değerleri izleyin. - Sıraları dahil olmak üzere
hasher.combineiçin sağlanan değerleri izleyin. - Ortak tanımı tek bir değişmez bileşen değere taşıyın ya da her iki uygulamanın da açıkça aynı bileşenleri kullandığından emin olun.
- Aynı test verileriyle eşitlik yasalarını, eşit değerlerin hashlerini, küme üyeliğini ve sözlük aramasını denetleyin.
Eşitliği hashValue değerlerini karşılaştırarak uygulamayın. Eşit değerlerin hashleri eşit olmalıdır; ancak hashlerin eşleşmesi eşitliği kanıtlamaz. == için temel, eşitliğin tüm bileşenleridir. Aynı şekilde, eşit olmayan her çiftin farklı hashleri olmasını şart koşan bir test, hashleme sözleşmesinin garanti ettiğinden fazlasını ister. Hashable
Bu sözleşmeyi denetlerken hashleri yalnızca mevcut çalıştırma içinde karşılaştırın. Swift’in Hasher türü rastgeleleştirilmiş bir başlangıç değeri kullanır ve algoritması standart kitaplık sürümleri arasında değişebilir. Bu nedenle dünkü çalıştırmadan yazdırılmış bir tamsayı, bugünkü çalıştırma için beklenen sonuç olarak uygun değildir. Hasher
Sonlu sayıdaki örnek denetimleri yararlı regresyon denetimleridir, ancak olası her dize ve boyut için kanıt oluşturmaz. Yapısal gerekçe de önemlidir: Her iki dış yöntem de seçilen kuralı eksiksiz ifade eden saklı alanlara sahip, aynı değişmez Components değerine yönlendirilir.
Eşzamanlılık gerekliliklerini eşitlikten ayrı tutun
Örnek, her iki yapıyı da Sendable olarak bildirir; çünkü saklanan değerleri dizeler, tamsayılar ve Sendable olan başka bir yapıdır. Swift, Sendable yapıların Sendable saklanan özellikler içermesi gerektiğini belgeler. Sendable olma, değerlerin eşzamanlılık alanları arasında aktarılmasıyla ilgilidir; hangi değerlerin eşit sayılacağını tanımlamaz. Sendable, The Swift Programming Language — Eşzamanlılık
Bu yerel hesaplama görev veya aktör gerektirmez. Daha sonra eşzamanlı çalışan masaüstü kodunun parçası olursa, eşitlik ve hashleme işlemlerini anahtarın saklanan bileşenleri üzerinde eşzamanlı olarak çalışır halde tutun. Paylaşılan değiştirilebilir koleksiyonları ayrı bir eşzamanlılık tasarımı konusu olarak ele alın. Dil kılavuzu, değiştirilebilir duruma erişimi koordine etmek için aktörleri ve yalıtımı kullanılan mekanizmalar olarak açıklar. The Swift Programming Language — Eşzamanlılık
Bu yaklaşımı uyarlarken Foundation sınırını da göz önünde bulundurun: NSObject alt sınıfı, devralınan eşitlik ve hashleme davranışını kullanır. Apple'ın desteklediği özelleştirme yolu, isEqual(_:) ve hash yöntemlerini birlikte geçersiz kılmaktır. Bu örnekteki düz Swift yapısı == ve hash(into:) kullanır. Yaygın Protokolleri Benimseme
Yerel bir döşeme anahtarı için sürdürülebilir sonuç; yazılı bir karşılaştırma kuralı, bu kuralın tek bir değişmez gösterimi ve gerçek koleksiyon aramalarını çalıştıran denetimlerdir. Tanım değiştiğinde bileşen gösterimini ve örneklerini birlikte güncelleyin; böylece eşitlik ve hashleme aynı değeri tanımlamayı sürdürür.
Kaynaklar
- Hashable — Apple
- Equatable — Apple
- Hasher — Apple
- Yaygın Protokolleri Benimseme — Apple
- folding(options:locale:) — StringProtocol — Apple
- folding(options:locale:) — NSString — Apple
- Sendable — Apple
- Protokoller — The Swift Programming Language, Swift.org
- Eşzamanlılık — The Swift Programming Language, Swift.org
- Başlarken — Swift.org
- Swift'i Yükleme — macOS — Swift.org