Untuk menjaga nilai Swift sintetis tetap unik tanpa mengubah urutan penyisipannya, pindai array masukan sekali, catat kunci yang diterima dalam set, lalu tambahkan hanya kemunculan pertama ke array keluaran. Panduan ini membuat perhitungan lokal sederhana yang dapat Anda jalankan di Mac.

Tugasnya adalah memproses daftar berurutan berisi nilai bentuk sintetis, menghapus nilai yang berulang, dan menghitung total dari entri yang dipertahankan. Semuanya berlangsung di memori; contoh ini tidak membaca masukan eksternal dan tidak menyimpan hasil perhitungan.

Tentukan arti “unik” sebelum memilih koleksi

Array Swift mempertahankan urutan dan mengizinkan duplikat. Set berisi nilai unik, tetapi tidak memiliki urutan yang ditentukan. Karena itu, mengubah array menjadi set lalu mengubahnya kembali menjadi array tidak mempertahankan urutan kemunculan pertama. Mengurutkannya setelah itu menetapkan urutan pengurutan yang mungkin berbeda dari urutan penyisipan. Perilaku koleksi ini didokumentasikan dalam Collection Types milik Swift.

Dalam contoh ini, setiap nilai sintetis memiliki nama bentuk dan ukuran. Kontrak bawaan menganggap dua entri sebagai duplikat hanya jika kedua propertinya cocok. Karena itu, lingkaran berukuran 12 dan lingkaran berukuran 20 tetap menjadi nilai terpisah.

Pertanyaan tentang kontrak Aturan untuk perhitungan ini
Apa yang menentukan urutan masukan? Posisi dalam array yang diberikan
Apa yang membuat dua nilai menjadi duplikat? Bentuk dan ukuran yang sama
Kemunculan mana yang dipertahankan? Kemunculan pertama
Apakah duplikat yang muncul belakangan memindahkan nilai yang dipertahankan? Tidak
Apa yang terjadi jika masukannya kosong? Mengembalikan array kosong dan total nol
Nilai mana yang dihitung ke dalam total? Hanya nilai yang dipertahankan

Kontrak ini mencegah kesalahan umum: menghapus entri berdasarkan nama bentuk saat perhitungan membutuhkan kombinasi bentuk dan ukuran yang berbeda. Contoh ini juga menghitung hasil berdasarkan bentuk saja agar Anda dapat melihat dampak perubahan aturan tersebut.

Buat contoh Swift lokal yang lengkap di Mac Anda

Anda memerlukan toolchain Swift yang sudah terpasang. Di Terminal, jalankan swift --version untuk memastikan perintah tersebut tersedia. Tutorial resmi Swift Build a Command-line Tool mendokumentasikan pemeriksaan itu, pembuatan paket executable, dan cara menjalankan paket.

Buat direktori baru untuk contoh ini, lalu inisialisasi paket executable:

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

Ganti berkas sumber executable yang dibuat di bawah Sources dengan program lengkap berikut. Nama berkas dan tata letak direktori yang dihasilkan dapat berbeda sesuai toolchain; ganti titik masuk yang sudah ada, alih-alih menambahkan titik masuk kedua. Program ini menggunakan titik masuk @main, jadi letakkan dalam berkas bernama OrderDemo.swift, dan ubah nama main.swift yang dihasilkan jika perlu. Tutorial baris perintah Swift menjelaskan pilihan antara berkas main.swift dan titik masuk @main.

Tidak diperlukan impor atau dependensi paket. Program ini menggunakan tipe dan fungsi pustaka standar.

struct Tile: Hashable {
    let shape: String
    let size: Int
}

func uniqueFirst<Value, Key: Hashable>(
    _ values: [Value],
    by key: (Value) -> Key
) -> [Value] {
    var seen: Set<Key> = []
    var result: [Value] = []

    for value in values {
        if seen.insert(key(value)).inserted {
            result.append(value)
        }
    }

    return result
}

@main
struct OrderDemo {
    static func main() {
        let input = [
            Tile(shape: "circle", size: 12),
            Tile(shape: "square", size: 8),
            Tile(shape: "circle", size: 12),
            Tile(shape: "circle", size: 20),
            Tile(shape: "square", size: 8)
        ]

        let retained = uniqueFirst(input, by: { $0 })
        let total = retained.reduce(0) { $0 + $1.size }
        let skipped = input.count - retained.count

        let expected = [
            Tile(shape: "circle", size: 12),
            Tile(shape: "square", size: 8),
            Tile(shape: "circle", size: 20)
        ]
        precondition(retained == expected)
        precondition(total == 40)
        precondition(skipped == 2)

        let empty: [Tile] = []
        precondition(uniqueFirst(empty, by: { $0 }).isEmpty)
        precondition(
            uniqueFirst([input[0], input[0]], by: { $0 })
                == [input[0]]
        )

        let byShape = uniqueFirst(input, by: { $0.shape })
        precondition(byShape == [input[0], input[1]])
        let shapeTotal = byShape.reduce(0) { $0 + $1.size }
        precondition(shapeTotal == 20)

        for tile in retained {
            print("\(tile.shape): \(tile.size)")
        }
        print("Total: \(total)")
        print("Skipped duplicates: \(skipped)")
        print("Shape-only total: \(shapeTotal)")
    }
}

Dari direktori paket, jalankan:

swift run OrderDemo

Keluaran program yang diharapkan, setelah pesan build apa pun, ditampilkan di bawah. Program ini diperiksa dengan merujuk dokumentasi API yang diperoleh, tetapi tidak dijalankan di sini karena lingkungan riset ini tidak memiliki executable Swift.

circle: 12
square: 8
circle: 20
Total: 40
Skipped duplicates: 2
Shape-only total: 20

Mengapa pendekatan array dan set mempertahankan urutan?

Array menjadi satu-satunya sumber urutan keluaran. Perulangan mengunjungi setiap posisi masukan secara berurutan, dan append(_:) menempatkan nilai yang diterima di akhir hasil. Set hanya menjawab apakah kunci yang dipilih sudah pernah muncul; program tidak pernah melakukan iterasi atas isinya.

Ekspresi yang menentukan adalah seen.insert(key(value)).inserted. Swift mendokumentasikan bahwa insert(_:) mengembalikan inserted sebagai true untuk anggota baru dan false jika anggota yang sama sudah ada. Anggota yang sudah ada tetap tidak berubah. Nilai kembalian itu memungkinkan satu operasi menentukan apakah nilai asli perlu ditambahkan. Lihat dokumentasi penyisipan Set dari Apple.

Untuk masukan yang diberikan, lingkaran pertama dan persegi diterima. Lingkaran kedua yang identik dilewati. Lingkaran yang lebih besar diterima karena nilai lengkapnya berbeda, sedangkan persegi yang berulang dilewati. Dengan demikian, ukuran yang dipertahankan adalah 12 + 8 + 20 = 40.

Implementasi ini mempertahankan invarian yang berguna: setelah setiap iterasi, hasil berisi tepat nilai paling awal yang ditemui untuk setiap kunci dalam seen, sesuai urutan kemunculannya. Kunci baru memperluas kedua koleksi; kunci berulang tidak mengubah keduanya. Penalaran ini menjelaskan jaminan urutan tanpa bergantung pada cara set menyimpan anggotanya.

Bagaimana nilai Swift khusus menyatakan kesetaraan?

Deklarasi struct Tile: Hashable meminta konformansi yang disintesis. Kedua properti tersimpannya, String dan Int, dapat di-hash, sehingga Swift dapat menyintesis perilaku yang diperlukan. Apple mendokumentasikan syarat sintesis ini dalam Hashable.

Menggunakan { $0 } sebagai kunci membuat seluruh nilai Tile menentukan keunikan. Menggunakan { $0.shape } mengubah kontrak menjadi satu entri yang dipertahankan untuk setiap bentuk. Dalam contoh ini, aturan tersebut menghapus lingkaran berukuran 20 yang muncul belakangan sehingga totalnya turun menjadi 20. Ini merupakan perbandingan yang disengaja antara dua aturan, bukan implementasi kedua dari aturan yang sama.

Jaga agar pemilihan kunci tetap deterministik selama perhitungan. Turunkan kunci dari nilai yang sedang diproses, bukan dari penghitung atau variabel eksternal yang berubah. Jika tidak, masukan yang sama dapat memperoleh kunci berbeda dan tetap bertahan sebagai entri terpisah.

Jika Anda menerapkan kesetaraan dan hashing secara manual, gunakan properti esensial yang sama untuk keduanya. Apple mensyaratkan agar instans yang setara memasukkan nilai yang sama ke hasher dalam urutan yang sama. Hindari menggunakan hashValue itu sendiri sebagai kunci keunikan: hash bukan pengganti kontrak kesetaraan nilai. Dengan mempertahankan kunci yang benar-benar dapat di-hash, set dapat menerapkan kontrak tersebut. Lihat persyaratan konformansi Hashable.

Kapan sebaiknya Anda menggunakan koleksi berurutan?

Untuk satu perhitungan ini, array ditambah set lokal membuat perilakunya tetap jelas. Jika aplikasi berulang kali menambahkan, menghapus, mengurutkan ulang, dan mencari anggota unik, koleksi berurutan khusus dapat memudahkan pemeliharaan operasi tersebut.

Pilihan Kondisi yang sesuai Batasan terkait
Array ditambah set Satu kali pemrosesan nilai Swift sintetis, mungkin menggunakan kunci yang dipilih Fungsi Anda menentukan perilaku kemunculan pertama
OrderedSet dari Swift Collections Penggunaan berkelanjutan koleksi berurutan yang berisi nilai unik dan dapat di-hash Memerlukan paket Swift Collections terpisah
Foundation NSOrderedSet API sudah bekerja dengan koleksi objek berurutan Foundation Kelas yang merepresentasikan koleksi berurutan statis; operasi mutabel menggunakan NSMutableOrderedSet

Dokumentasi resmi Swift Collections menjelaskan OrderedSet sebagai koleksi terurut yang berisi elemen unik dan diimpor melalui OrderedCollections. Koleksi ini menyediakan operasi yang mempertimbangkan posisi, seperti append(_:) dan insert(_:at:). Keduanya adalah API paket, jadi menambahkan import OrderedCollections saja tidak mengonfigurasi proyek yang tanpa dependensi. Lihat OrderedSet.

Dokumentasi Foundation dari Apple menjelaskan NSOrderedSet sebagai koleksi statis berurutan yang berisi objek berbeda dan menyebut NSMutableOrderedSet sebagai padanannya yang dapat diubah. Untuk perhitungan nilai Swift native di atas, fungsi generik menyatakan kunci kesetaraan yang dipilih secara langsung tanpa memperkenalkan koleksi objek tersebut. Lihat NSOrderedSet.

Apa yang berubah saat perhitungan digunakan oleh antarmuka desktop?

Contoh lengkap ini berjalan secara sinkron dan memiliki koleksi sementara di dalam fungsi. Untuk daftar sintetis kecil, cara ini menghasilkan satu urutan pemrosesan yang jelas tanpa memperkenalkan task.

Dalam antarmuka macOS, pisahkan state dan pembaruan yang berhubungan dengan UI secara tepat. Apple mendokumentasikan MainActor sebagai aktor global yang eksekutornya setara dengan antrean dispatch utama; panduan konkurensi Swift menjelaskan cara menerapkan @MainActor pada fungsi dan tipe yang mengakses state antarmuka. Lihat MainActor dan Concurrency.

Konkurensi juga memerlukan definisi urutan yang jelas. Swift tidak menjamin urutan penyelesaian task anak dalam grup task. Karena itu, menambahkan hasil saat task selesai akan mempertahankan urutan penyelesaian, yang bisa berbeda dari urutan masukan asli. Panduan konkurensi resmi mendokumentasikan batasan ini.

Jika versi mendatang menjalankan perhitungan sintetis yang independen secara bersamaan, sertakan posisi setiap masukan bersama hasilnya, pulihkan urutan berdasarkan posisi masukan setelah pengumpulan, lalu lakukan proses keunikan. Ini adalah rancangan yang diturunkan dari batasan urutan penyelesaian yang telah didokumentasikan. Pertahankan contoh kecil saat ini agar tetap sinkron kecuali ada pekerjaan tambahan yang membenarkan koordinasi ekstra.

Periksa kontrak sebelum menggunakan kembali fungsi

Program ini mencakup pemeriksaan untuk urutan yang dipertahankan secara tepat, masukan kosong, nilai identik yang berulang, dan aturan alternatif yang hanya mempertimbangkan bentuk. Pemeriksaan ini menargetkan keputusan yang paling mungkin mengubah makna perhitungan.

Saat mengadaptasinya, periksa tiga kondisi: masukan harus sudah memiliki urutan yang bermakna, kunci harus menyatakan aturan kesetaraan yang Anda maksud, dan mempertahankan kemunculan pertama haruslah sesuai. Fungsi ini sengaja melewati kecocokan yang muncul belakangan; fungsi ini tidak menggabungkan ukurannya dan tidak mengganti nilai yang dipertahankan. Jumlah yang dilewati membuat dampak tersebut terlihat.

Untuk daftar bentuk sintetis yang diberikan, hasil akhirnya adalah tiga nilai dalam urutan kemunculan pertama dengan total 40. Biarkan array mengatur urutan, set menangani keanggotaan, dan kunci yang dipilih menentukan arti “unik”.

Sumber