Gunakan Dictionary(grouping:by:) untuk mengumpulkan nilai Swift sintetik mengikut kunci, kemudian gunakan mapValues dan reduce untuk mengira jumlah setiap kumpulan. Tutorial Mac ini juga menunjukkan pengumpulan langsung serta semakan untuk input kosong, jumlah sifar dan limpahan integer.

Tugasnya ialah menukarkan tatasusunan dalam memori yang mengandungi label bentuk neutral dan kuantiti nombor bulat kepada satu kamus [String: Int]. Semua input ialah literal sintetik. Pengiraan ini tidak membaca rekod luaran, membuat permintaan rangkaian atau menyimpan hasil.

Takrifkan maksud satu jumlah bagi setiap kunci

Sebelum memilih kaedah koleksi, takrifkan kontrak hasil. Di sini, setiap elemen input menyumbangkan kuantitinya tepat sekali kepada jumlah bagi labelnya. Label menggunakan kesamaan rentetan Swift biasa; program ini tidak melakukan pemangkasan atau penukaran huruf besar kecil. Kunci kamus mesti mematuhi Hashable, dan Swift menyokong rentetan sebagai kunci. Lihat dokumentasi Dictionary oleh Apple.

Input ilustrasi mengandungi dua nilai bulatan, dua nilai segi empat sama dan satu nilai segi tiga. Jumlah yang dimaksudkan cukup mudah untuk disemak secara manual:

Label Kuantiti input Jumlah dijangka
circle 4, 6 10
square 3, 5 8
triangle 0 0

Contoh ini menggunakan peraturan berikut:

  • Input kosong menghasilkan kamus kosong.
  • Label yang muncul dengan kuantiti sifar kekal ada dengan jumlah sifar.
  • Kuantiti negatif dibenarkan dan dijumlahkan seperti biasa.
  • Label yang tiada dalam input juga tiada dalam hasil.
  • Setiap jumlah perantaraan mesti muat dalam Int; jika tidak, fungsi akan melemparkan ralat dan tidak memulangkan sebarang jumlah.

Perbezaan antara label yang tiada dengan jumlah sifar adalah penting. Label yang tiada bermaksud tiada input yang sepadan. Label yang ada dengan jumlah sifar mungkin mewakili sifar yang dinyatakan secara jelas atau kuantiti yang saling membatalkan. Kekalkan perbezaan ini melainkan pembaca hasil benar-benar memerlukan konvensyen yang berbeza.

Jalankan contoh lengkap pada Mac

Gunakan toolchain Swift yang telah dipasang. Panduan pemasangan macOS di Swift.org menerangkan persediaan toolchain serta arahan untuk mencipta dan menjalankan pakej boleh laku. Setelah toolchain sedia, buka Terminal dan cipta projek setempat yang kecil:

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

Buka fail sumber boleh laku yang dijana di bawah Sources dan gantikan kandungannya dengan program lengkap di bawah. Fail yang dijana mungkin berada dalam subfolder yang dinamakan mengikut sasaran. Kekalkan manifes pakej seperti yang dijana; program ini tidak memerlukan kebergantungan atau import tambahan kerana jenis dan kaedah pengiraannya tergolong dalam pustaka standard Swift.

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)")
}

Dari direktori pakej, jalankan program boleh laku menggunakan arahan yang didokumenkan dalam panduan macOS Swift.org:

swift run SyntheticTotals

Output program yang dijangka, selain mesej binaan, ialah:

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

Program ini merangkumi semakan untuk jumlah contoh, input kosong, pembatalan kepada sifar dan limpahan yang disengajakan. Hasil ini dijangka berdasarkan sampel dan API yang didokumenkan. Kod telah diperiksa tetapi tidak dijalankan semasa penyediaan kerana persekitaran yang tersedia tidak mempunyai pengkompil Swift.

Bagaimanakah pengumpulan diikuti penjumlahan berfungsi?

Fungsi pertama sengaja memisahkan tugas kepada beberapa peringkat yang jelas. Dictionary(grouping: values, by: { $0.key }) mencipta kamus yang nilainya ialah tatasusunan elemen yang sepadan. Apple mendokumenkan bahawa setiap kumpulan yang terhasil tidak kosong dan mengekalkan susunan elemen daripada jujukan sumber. Lihat init(grouping:by:).

Bagi sampel ini, jenis perantara ialah [String: [SyntheticValue]]. Kumpulan bulatan mengandungi kuantiti 4 dan 6; kumpulan segi empat sama mengandungi 3 dan 5; kumpulan segi tiga mengandungi 0. Perwakilan perantara ini berguna apabila bahagian lain dalam pengiraan setempat juga perlu memeriksa ahli setiap kumpulan.

Seterusnya, mapValues mengekalkan kunci kamus dan mengubah setiap tatasusunan kumpulan menjadi satu integer. Pengekalan kunci ini ialah tingkah laku mapValues(_:) yang didokumenkan. Hal ini juga menerangkan sebab entri segi tiga kekal walaupun jumlahnya sifar.

Dalam transformasi itu, reduce(0) bermula dengan sifar dan menghantar setiap elemen berturutan kepada penutupan penggabung. Penutupan itu memulangkan jumlah terkumpul yang seterusnya. Apple mendokumenkan pengurangan berjujukan dan menyatakan bahawa pengurangan jujukan kosong memulangkan nilai awal. Lihat reduce(::).

Di sini terdapat dua kes kosong yang berbeza. Tatasusunan kuantiti kosong yang dikurangkan bermula daripada sifar akan menghasilkan sifar. Namun, pengumpulan input kosong tidak menghasilkan sebarang kumpulan, jadi fungsi lengkap memulangkan kamus kosong dan bukannya mencipta label dengan jumlah sifar.

Bilakah jumlah patut dikumpulkan secara langsung?

Gunakan totalDirectly apabila jumlah akhir sahaja yang diperlukan. Akumulatornya bermula sebagai kamus [String: Int] kosong. Bagi setiap elemen input, kaedah ini mencari jumlah sebelumnya untuk label tersebut, menambah kuantiti dan menyimpan jumlah yang dikemas kini.

Carian dengan nilai lalai membekalkan sifar apabila kunci tiada. Membaca nilai lalai sahaja tidak mencipta entri; penetapan pada baris seterusnya menyimpan jumlah yang dikira. Dokumentasi subskrip nilai lalai oleh Apple menerangkan carian sandaran serta kemas kini di tempat bagi nilai dengan semantik nilai.

reduce(into:) memberikan penutupannya akses boleh ubah kepada akumulator. Apple mengesyorkan bentuk ini berbanding reduce biasa apabila membina hasil dengan tingkah laku salin semasa tulis, seperti kamus. Lihat reduce(into:_:).

Keperluan langkah seterusnya Pilih Sebab
Ahli kumpulan dan jumlahnya Kumpulkan, kemudian jumlahkan Tatasusunan perantara boleh diperiksa atau digunakan semula.
Hanya satu integer bagi setiap label Pengumpulan langsung Kaedah ini mengelakkan pembinaan tatasusunan kumpulan perantara.
Output teks yang boleh diulang dengan susunan sama Kedua-duanya, kemudian isih kunci Susunan output ialah keputusan berasingan daripada pengagregatan.

Perbezaan penyimpanan perantara ini berdasarkan kod: pengumpulan mengekalkan semua elemen input dalam tatasusunan kumpulan, manakala pengumpulan langsung mengekalkan satu jumlah terkumpul bagi setiap kunci unik. Ini ialah perbandingan pelaksanaan, bukannya dakwaan prestasi yang diukur. Sampel ini tidak menyediakan penanda aras masa.

Bagaimanakah limpahan dan output yang stabil dikendalikan?

checkedAdd memanggil addingReportingOverflow, yang memulangkan hasil separa dan tanda limpahan. Apple menyatakan bahawa nilai separa mengandungi jumlah lengkap hanya apabila tanda itu bernilai palsu. Apabila limpahan berlaku, nilai yang dipulangkan dipotong. Oleh itu, program melemparkan ralat sebelum menyimpannya. Lihat addingReportingOverflow(_:).

Kontrak ini menyemak setiap penambahan mengikut susunan input dalam setiap kunci. Contohnya, kuantiti Int.max, 1 dan -1 bagi satu label ditolak pada penambahan kedua, walaupun jumlah matematik akhirnya muat dalam Int. Input sedemikian memerlukan strategi aritmetik yang berbeza; menerima hasil perantaraan yang dipotong akan mengubah kontrak.

Jika beberapa kumpulan mengalami limpahan, pelaksanaan pengumpulan tidak menjamin label mana yang ralatnya akan muncul dahulu: susunan lelaran kamus tidak dapat diramal. Pelaksanaan langsung menemui elemen mengikut susunan sumber. Kedua-duanya menolak pengiraan yang melimpah, tetapi kunci pertama yang dilaporkan mungkin berbeza.

Mengisih kunci hasil menjadikan jumlah yang dipaparkan dapat diramal. Kamus Swift tidak tersusun, jadi mencetak kamus secara langsung tidak patut digunakan untuk menetapkan susunan persembahan. Apple menerangkan batas susunan ini dalam dokumentasi Dictionary. Contoh ini mengisih kunci hanya selepas pengagregatan, tanpa mengubah pengiraan.

Adakah Foundation atau konkurensi Swift mengubah pengiraan ini?

Foundation tidak diperlukan untuk kontrak nilai ini. Apple mendokumenkan NSDictionary sebagai alternatif apabila semantik rujukan diperlukan. Di sini, kamus Swift bertip sudah mewakili pemetaan yang diperlukan daripada label kepada jumlah integer. Lihat dokumentasi NSDictionary Foundation.

Pengiraan ini segerak dan mengekalkan akumulator boleh ubahnya di dalam fungsi. SyntheticValue dalam sampel secara jelas mematuhi Sendable; ini menyatakan kontrak nilai berkaitan konkurensi, bukannya permintaan untuk menjalankan pengurangan secara serentak. Panduan Konkurensi Swift menerangkan nilai yang boleh dihantar serta keperluan bagi struktur yang sifat tersimpannya boleh dihantar.

Untuk pengiraan desktop yang kecil ini, panggil mana-mana fungsi secara langsung dan periksa kamus yang dipulangkannya. Pilih pengumpulan apabila ahli kumpulan masih berguna, atau pengumpulan langsung apabila jumlah sahaja mencukupi. Kekalkan peraturan input kosong dan jumlah sifar, tolak limpahan aritmetik secara jelas, dan isih hanya semasa mempersembahkan hasil.

Sumber