Dictionary(grouping:by:) kullanarak sentetik Swift değerlerini anahtara göre toplayın, ardından her toplamı hesaplamak için mapValues ve reduce kullanın. Bu Mac öğreticisi ayrıca doğrudan biriktirmeyi ve boş girdiyi, sıfır toplamları ve tamsayı taşmasını denetlemeyi gösterir.

Görev, nötr biçim etiketleri ve tam sayı miktarlarından oluşan bellekteki bir diziyi tek bir [String: Int] sözlüğüne dönüştürmektir. Tüm girdiler sentetik sabit değerlerdir. Hesaplama harici kayıtları okumaz, ağ isteği yapmaz ve sonuç kaydetmez.

Anahtar başına tek bir toplamın ne anlama geldiğini tanımlayın

Koleksiyon yöntemlerini seçmeden önce sonuç sözleşmesini tanımlayın. Burada her girdi öğesi, etiketine ait toplama miktarını tam olarak bir kez ekler. Etiketlerde Swift’in olağan dize eşitliği kullanılır; program kırpma veya büyük-küçük harf dönüştürme işlemi yapmaz. Sözlük anahtarlarının Hashable ile uyumlu olması gerekir; Swift, dizelerin anahtar olarak kullanılmasını destekler. Apple’ın Dictionary belgelerine bakın.

Örnek girdi iki daire değeri, iki kare değeri ve bir üçgen değeri içerir. Amaçlanan toplamları elle kontrol etmek kolaydır:

Etiket Girdi miktarları Beklenen toplam
circle 4, 6 10
square 3, 5 8
triangle 0 0

Bu örnekte şu kurallar geçerlidir:

  • Boş girdi boş bir sözlük üretir.
  • Sıfır miktarla görünen bir etiket, sıfır toplamla sonuçta yer almaya devam eder.
  • Negatif miktarlara izin verilir ve olağan toplama işlemiyle hesaba katılır.
  • Girdide bulunmayan bir etiket sonuçta yer almaz.
  • Her ara toplam Int türüne sığmalıdır; aksi hâlde işlev hata verir ve hiçbir toplam döndürmez.

Bir etiketin bulunmaması ile toplamının sıfır olması arasındaki ayrım önemlidir. Eksik bir etiket, eşleşen girdi olmadığı anlamına gelir. Sıfır değerli mevcut bir etiket, açıkça belirtilmiş bir sıfırı veya birbirini götüren miktarları temsil edebilir. Sonucu okuyacak kişi özellikle farklı bir kural gerektirmedikçe bu ayrımı koruyun.

Mac’te eksiksiz örneği çalıştırın

Yüklü bir Swift araç zinciri kullanın. Swift.org’un macOS yükleme kılavuzu, araç zinciri kurulumunu ve yürütülebilir paket oluşturup çalıştırma komutlarını açıklar. Araç zinciri hazır olduğunda Terminal’i açın ve küçük bir yerel proje oluşturun:

mkdir SyntheticTotals
cd SyntheticTotals
swift package init --name SyntheticTotals --type executable

Sources altındaki oluşturulan yürütülebilir kaynak dosyasını açın ve içeriğini aşağıdaki eksiksiz programla değiştirin. Oluşturulan dosya, hedefin adını taşıyan bir alt klasörde olabilir. Paket manifestini oluşturulduğu gibi bırakın; bu programın ek bağımlılığa veya içe aktarmaya ihtiyacı yoktur, çünkü türleri ve hesaplama yöntemleri Swift’in standart kitaplığına aittir.

struct SyntheticValue: Sendable {
    let key: String
    let quantity: Int
}

enum TotalError: Error {
    case overflow(key: String)
}

func checkedAdd(_ total: Int, _ value: SyntheticValue) throws -> Int {
    let addition = total.addingReportingOverflow(value.quantity)
    guard !addition.overflow else {
        throw TotalError.overflow(key: value.key)
    }
    return addition.partialValue
}

func groupThenTotal(_ values: [SyntheticValue]) throws -> [String: Int] {
    let groups = Dictionary(grouping: values, by: { $0.key })
    return try groups.mapValues { group in
        try group.reduce(0) { total, value in
            try checkedAdd(total, value)
        }
    }
}

func totalDirectly(_ values: [SyntheticValue]) throws -> [String: Int] {
    return try values.reduce(into: [String: Int]()) { totals, value in
        let previous = totals[value.key, default: 0]
        totals[value.key] = try checkedAdd(previous, value)
    }
}

let sample: [SyntheticValue] = [
    SyntheticValue(key: "circle", quantity: 4),
    SyntheticValue(key: "square", quantity: 3),
    SyntheticValue(key: "circle", quantity: 6),
    SyntheticValue(key: "triangle", quantity: 0),
    SyntheticValue(key: "square", quantity: 5)
]

let expected = ["circle": 10, "square": 8, "triangle": 0]

 do {
    let groupedTotals = try groupThenTotal(sample)
    let directTotals = try totalDirectly(sample)
    precondition(groupedTotals == expected)
    precondition(directTotals == expected)

    let emptyGrouped = try groupThenTotal([])
    let emptyDirect = try totalDirectly([])
    precondition(emptyGrouped.isEmpty && emptyDirect.isEmpty)

    let cancellation = [
        SyntheticValue(key: "circle", quantity: 2),
        SyntheticValue(key: "circle", quantity: -2)
    ]
    let zeroGrouped = try groupThenTotal(cancellation)
    let zeroDirect = try totalDirectly(cancellation)
    precondition(zeroGrouped == ["circle": 0])
    precondition(zeroDirect == ["circle": 0])

    for key in groupedTotals.keys.sorted() {
        if let total = groupedTotals[key] {
            print("\(key): \(total)")
        }
    }

    let tooLarge = [
        SyntheticValue(key: "circle", quantity: Int.max),
        SyntheticValue(key: "circle", quantity: 1)
    ]
    do {
        _ = try totalDirectly(tooLarge)
        preconditionFailure("Expected overflow to be reported")
    } catch TotalError.overflow(let key) {
        print("Overflow check: rejected \(key)")
    }
} catch {
    fatalError("Unexpected calculation failure: \(error)")
}

Paket dizininden, Swift.org’un macOS kılavuzunda belgelenen komutu kullanarak yürütülebilir dosyayı çalıştırın:

swift run SyntheticTotals

Derleme mesajları dışında beklenen program çıktısı şöyledir:

circle: 10
square: 8
triangle: 0
Overflow check: rejected circle

Programda hesaplanan toplamlar, boş girdi, sıfıra iptal ve kasıtlı taşma için denetimler bulunur. Bunlar örnekten ve belgelenmiş API’lerden elde edilen beklenen sonuçlardır. Kod incelendi, ancak hazırlık sırasında kullanılabilir ortamda Swift derleyicisi olmadığı için çalıştırılmadı.

Gruplama ve ardından indirgeme nasıl çalışır?

İlk işlev, görevi bilinçli olarak görünür aşamalara ayırır. Dictionary(grouping: values, by: { $0.key }), eşleşen öğe dizilerini değer olarak içeren bir sözlük oluşturur. Apple, her grubun boş olmadığını ve öğelerin kaynak dizideki sıralamasını koruduğunu belgeliyor. init(grouping:by:) sayfasına bakın.

Örnek için ara tür [String: [SyntheticValue]] olur. Daire grubu 4 ve 6 miktarlarını; kare grubu 3 ve 5 miktarlarını; üçgen grubu ise 0 miktarını içerir. Yerel bir hesaplamanın başka bir bölümü her grubun üyelerini de inceleyecekse bu ara gösterim kullanışlıdır.

Ardından mapValues, sözlüğün anahtarlarını korur ve her grup dizisini tek bir tamsayıya dönüştürür. Anahtarların korunması, mapValues(_:) için belgelenmiş davranıştır. Bu, toplamı sıfır olsa bile üçgen girdisinin neden korunduğunu da açıklar.

Bu dönüşümün içinde reduce(0), sıfırdan başlar ve her ardışık öğeyi birleştirme kapanışına aktarır. Kapanış, sonraki geçerli toplamı döndürür. Apple sıralı indirgemeyi belgeliyor ve boş bir dizinin indirgenmesinin başlangıç değerini döndürdüğünü belirtiyor. reduce(::) sayfasına bakın.

Burada iki farklı boş durum vardır. Boş bir miktar dizisi sıfırdan indirgenirse sonuç sıfır olur. Ancak boş bir girdinin gruplanması hiçbir grup oluşturmaz; dolayısıyla eksiksiz işlev, sıfır toplamlı yeni bir etiket üretmek yerine boş bir sözlük döndürür.

Toplamları doğrudan ne zaman biriktirmelisiniz?

Sonuç olarak yalnızca toplamlar gerekiyorsa totalDirectly kullanın. Biriktiricisi boş bir [String: Int] sözlüğüyle başlar. Her girdi öğesi için etiketin önceki toplamına bakar, miktarı ekler ve güncellenmiş toplamı kaydeder.

Varsayılan değerli arama, anahtar bulunamadığında sıfır sağlar. Varsayılan değeri okumak tek başına bir girdi oluşturmaz; sonraki satırdaki atama hesaplanan toplamı kaydeder. Apple’ın varsayılan değerli alt simge belgeleri, hem yedek değerli aramayı hem de değer semantiğine sahip değerler için yerinde güncellemeleri açıklar.

reduce(into:), kapanışına biriktiriciye değiştirilebilir erişim sağlar. Apple, sözlük gibi yazma sırasında kopyalama davranışına sahip bir sonuç oluştururken bu biçimin sıradan reduce yerine kullanılmasını öneriyor. reduce(into:_:) sayfasına bakın.

Sonraki adımın ihtiyacı Seçim Gerekçe
Grup üyeleri ve toplamları Gruplayın, ardından indirgeyin Ara diziler incelenebilir veya yeniden kullanılabilir.
Etiket başına yalnızca bir tamsayı Doğrudan biriktirme Ara grup dizilerini oluşturmayı önler.
Tekrarlanabilir metin çıktısı Herhangi biri, ardından anahtarları sıralayın Çıktı sırası, toplamayı yapmaktan ayrı bir karardır.

Ara depolama farkı koddan kaynaklanır: gruplama tüm girdi öğelerini grup dizilerinde tutarken doğrudan biriktirme, farklı her anahtar için bir geçerli toplam tutar. Bu bir uygulama karşılaştırmasıdır; ölçülmüş bir performans iddiası değildir. Örnekte zamanlama ölçümü yoktur.

Taşma ve kararlı çıktıyla nasıl başa çıkarsınız?

checkedAdd, kısmi sonuç ve taşma bayrağı döndüren addingReportingOverflow işlevini çağırır. Apple, bayrak false olduğunda kısmi değerin tam toplamı içerdiğini belirtir. Taşma durumunda döndürülen kısmi değer kesilir. Bu nedenle program, değeri kaydetmeden önce hata fırlatır. addingReportingOverflow(_:) sayfasına bakın.

Bu sözleşme, her anahtar için girdilerin sırasına göre her toplamayı denetler. Örneğin, tek bir etikete ait Int.max, 1 ve -1 miktarları ikinci toplamada reddedilir; matematiksel nihai toplam Int türüne sığsa bile durum değişmez. Bu tür girdileri desteklemek farklı bir aritmetik stratejisi gerektirir; kesilmiş ara sonucu kabul etmek sözleşmeyi değiştirir.

Birden fazla grup taşarsa gruplama uygulaması, hangi etiketin hatasının önce görüneceğini garanti etmez: sözlük yineleme sırası öngörülemez. Doğrudan uygulama öğelerle kaynak sırasına göre karşılaşır. İkisi de taşan bir hesaplamayı reddeder, ancak ilk bildirilen anahtar farklı olabilir.

Sonucun anahtarlarını sıralamak, görüntülenen toplamları öngörülebilir kılar. Swift sözlüklerinin sırası yoktur; bu nedenle bir sözlüğü doğrudan yazdırmak sunum sırası belirlemek için kullanılmamalıdır. Apple, bu sıralama sınırını Dictionary belgelerinde açıklıyor. Örnek, toplamayı değiştirmeden yalnızca işlemden sonra anahtarları sıralar.

Foundation veya Swift eşzamanlılığı bu hesaplamayı değiştirir mi?

Bu değer sözleşmesi için Foundation gerekmez. Apple, başvuru semantiğinin gerektiği durumlarda NSDictionary’i alternatif olarak belgeliyor. Burada türü belirtilmiş bir Swift sözlüğü, etiketlerden tamsayı toplamlarına gereken eşlemeyi zaten temsil eder. Foundation’ın NSDictionary belgelerine bakın.

Hesaplama eşzamanlıdır ve değiştirilebilir biriktiricisini işlevin içinde tutar. Örnekteki SyntheticValue açıkça Sendable ile uyumludur; bu, eşzamanlılıkla ilgili bir değer sözleşmesini ifade eder, indirgemeyi eşzamanlı olarak çalıştırma isteğini değil. Swift’in Eşzamanlılık kılavuzu, gönderilebilir değerleri ve saklanan özellikleri gönderilebilir olan yapılar için gerekenleri açıklar.

Bu küçük masaüstü hesaplaması için iki işlevden birini doğrudan çağırın ve döndürdüğü sözlüğü inceleyin. Grup üyeleri işe yarıyorsa gruplamayı, toplamlar yeterliyse doğrudan biriktirmeyi seçin. Boş girdi ve sıfır toplam kurallarını koruyun, aritmetik taşmayı açıkça reddedin ve yalnızca sonucu sunarken sıralayın.

Kaynaklar