Ö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:

  1. Eşit olması gereken bir çift ve değerleri birbirinden ayırması gereken her alan için bir çift yazın.
  2. Normalleştirme ve türetilmiş özellikler dahil olmak üzere == tarafından karşılaştırılan gerçek değerleri izleyin.
  3. Sıraları dahil olmak üzere hasher.combine için sağlanan değerleri izleyin.
  4. 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.
  5. 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