Untuk mengisih tatasusunan Swift setempat mengikut beberapa medan, bandingkan setiap kekunci mengikut keutamaannya dan teruskan hanya apabila nilainya seri. Tentukan nilai sandaran pilihan secara berasingan, kemudian tetapkan kedudukan bagi nilai yang tiada. Contoh ini menyusun label bentuk sintetik untuk senarai desktop Mac.
Tentukan kontrak pengisihan sebelum menulis penutupan
Katakan senarai desktop mengandungi label bentuk dengan nombor kumpulan, kedudukan pilihan, kedudukan sandaran dan tajuk, yang semuanya pilihan. Tugasnya ialah menghasilkan satu susunan paparan yang boleh dijangka daripada nilai dalam memori ini.
Dua keputusan memerlukan peraturan yang berasingan. Nilai sandaran memilih nilai yang mewakili satu item: gunakan kedudukan pilihannya jika ada, jika tidak gunakan kedudukan sandarannya. Pemecah seri membandingkan medan lain hanya selepas dua item mempunyai kedudukan efektif yang sama. Operator nil-gabungan Swift, ??, menyokong keputusan pertama dengan memilih nilai yang dibalut atau nilai sandaran. Operator Asas — Operator Nil-Gabungan
Gunakan kontrak yang jelas ini untuk contoh:
| Keutamaan | Kekunci | Peraturan |
|---|---|---|
| 1 | Kumpulan | Nombor lebih kecil dahulu |
| 2 | Kedudukan efektif | preferredPosition ?? backupPosition |
| 3 | Kedudukan nilai tiada | Kedudukan yang tiada menyusul kedudukan yang ada dalam setiap kumpulan |
| 4 | Tajuk | Perbandingan rentetan Swift menaik apabila kedudukan seri |
| 5 | Pengecam | Pengecam lebih kecil dahulu apabila semua kekunci terdahulu seri |
Kedudukan nilai yang tiada ialah sebahagian daripada perbandingan kedudukan, bukannya medan berasingan yang mengatasi kumpulan. Item dalam kumpulan sifar tetap mendahului item dalam kumpulan satu walaupun item pertama tiada kedudukan.
Perbandingan tajuk sengaja menggunakan susunan rentetan biasa Swift untuk pengiraan sintetik ini. Kekalkan pilihan itu jika anda kemudian menggantikan penutupan dengan pembanding Foundation.
Tulis pengisihan berbilang medan yang lengkap dengan pengendalian nil yang jelas
Program lengkap berikut menggunakan pustaka standard Swift dan tidak memerlukan import. Jalankannya dalam persekitaran yang menyokong Swift pada Mac anda. Semua input ditakrifkan dalam program; pengiraan ini tidak menggunakan input luaran atau storan kekal. Operasi pengisihan mengembalikan tatasusunan baharu melalui sorted(by:). Dokumentasi Apple tentang sorted(by:)
struct ShapeItem {
let id: Int
let group: Int
let preferredPosition: Int?
let backupPosition: Int?
let title: String
var effectivePosition: Int? {
preferredPosition ?? backupPosition
}
}
func comesBefore(_ lhs: ShapeItem, _ rhs: ShapeItem) -> Bool {
if lhs.group != rhs.group {
return lhs.group < rhs.group
}
switch (lhs.effectivePosition, rhs.effectivePosition) {
case let (.some(left), .some(right)):
if left != right {
return left < right
}
case (.some(_), .none):
return true
case (.none, .some(_)):
return false
case (.none, .none):
break
}
if lhs.title != rhs.title {
return lhs.title < rhs.title
}
return lhs.id < rhs.id
}
let items = [
ShapeItem(id: 10, group: 1, preferredPosition: nil,
backupPosition: 2, title: "Circle"),
ShapeItem(id: 20, group: 1, preferredPosition: 1,
backupPosition: 9, title: "Square"),
ShapeItem(id: 30, group: 1, preferredPosition: nil,
backupPosition: nil, title: "Arc"),
ShapeItem(id: 40, group: 1, preferredPosition: 2,
backupPosition: 0, title: "Arc"),
ShapeItem(id: 50, group: 0, preferredPosition: nil,
backupPosition: nil, title: "Dot"),
ShapeItem(id: 60, group: 1, preferredPosition: 2,
backupPosition: nil, title: "Arc"),
ShapeItem(id: 70, group: 1, preferredPosition: 0,
backupPosition: 8, title: "Zero")
]
let ordered = items.sorted(by: comesBefore)
let expectedIDs = [50, 70, 20, 40, 60, 10, 30]
precondition(ordered.map(\.id) == expectedIDs)
precondition(ordered.count == items.count)
precondition(items.reversed().sorted(by: comesBefore).map(\.id)
== expectedIDs)
print(ordered.map(\.id))
Output yang dijangka:
[50, 70, 20, 40, 60, 10, 30]
Program dan susunan yang dijangka telah diperiksa, tetapi tidak dijalankan di sini kerana persekitaran penyelidikan tiada pengkompil Swift. Prasyarat yang disertakan membolehkan anda menyemak hasilnya apabila menjalankannya secara setempat.
Sebab setiap sampel berada pada kedudukan itu
Item 50 berada di tempat pertama kerana kumpulan sifar diutamakan sebelum kedudukan dipertimbangkan. Item 70 seterusnya menunjukkan bahawa sifar ialah kedudukan pilihan yang sah: nilai sandaran lapan tidak digunakan. Item 20 menggunakan kedudukan pilihannya, iaitu satu.
Item 40, 60 dan 10 semuanya mempunyai kedudukan efektif dua. Dua tajuk Arc mendahului Circle, dan pengecam menyelesaikan seri yang selebihnya antara 40 dan 60. Item 30 berada paling akhir kerana kedua-dua medan kedudukannya tiada dalam kumpulan satu.
Ini juga menjawab soalan lazim tentang nilai sandaran: item 40 tidak menggunakan kedudukan sandaran sifarnya apabila kedudukan pilihan seri dengan item lain. Di bawah kontrak ini, nilai sandaran hanya menggantikan nilai pilihan yang tiada. Jika nilai sandaran juga patut memecahkan seri, peraturan perbandingan tambahan diperlukan.
Pastikan pembanding sah apabila nilai seri
Swift menghendaki predikat pengisihan membentuk susunan lemah ketat. Dari segi praktikal, item tidak boleh mendahului dirinya sendiri, keputusan susunan mesti kekal konsisten merentas perbandingan, dan item yang setara mesti membentuk kumpulan yang konsisten. Apple mendokumenkan keperluan ini untuk sorted(by:). Keperluan predikat
Contoh ini menyemak sama ada kekunci terdahulu berbeza sebelum memulangkan hasil susunannya. Perbandingan sampai ke tajuk hanya apabila kumpulan dan kedudukan efektif seri, kemudian sampai ke pengecam hanya apabila tajuk juga seri. Jika pengecam dan semua kekunci terdahulu sama, perbandingan ketat terakhir memulangkan false.
Elakkan menggabungkan perbandingan medan bebas dengan ||. Satu item mungkin mempunyai kumpulan yang lebih awal tetapi kedudukan yang lebih lewat; item lain mungkin mempunyai gabungan yang bertentangan. Predikat berasaskan OR boleh menyatakan bahawa setiap item patut mendahului yang satu lagi. Pengendalian seri secara berurutan mengelakkan percanggahan itu.
Elakkan juga menggunakan <= sebagai perbandingan terakhir: nilai yang sama akan memulangkan true, sekali gus melanggar peraturan bahawa item tidak boleh mendahului dirinya sendiri. Pastikan setiap perbandingan hanya bergantung pada nilai yang dibekalkan dan peraturan pengisihan yang tetap.
Pilih antara seri yang stabil dengan pengecam terakhir
Pengisihan Swift yang didokumenkan adalah stabil: unsur yang dianggap setara oleh predikat mengekalkan susunan relatif inputnya. Jika itulah tingkah laku yang dikehendaki, gugurkan perbandingan pengecam terakhir dan pulangkan false selepas tajuk seri. Jaminan pengisihan stabil
Sebaliknya, contoh ini menggunakan pengecam sintetik yang unik untuk menyelesaikan seri yang selebihnya. Oleh itu, membalikkan input sampel tetap menghasilkan urutan pengecam yang sama. Sifat itu terhasil daripada kontrak dan nilai sampel ini. Jika pengecam berulang bersama semua kekunci lain, item tersebut kekal setara dan mengekalkan susunan inputnya.
Nyatakan peraturan yang sama dengan pembanding laluan kekunci Foundation
Foundation menyediakan KeyPathComparator untuk membandingkan sifat dan sorted(using:) untuk menggunakan satu jujukan pembanding. Pembanding pertama memberikan susunan utama; pembanding seterusnya memperincikan seri. API ini tersedia mulai macOS 12. KeyPathComparator, sorted(using:)
Untuk versi Foundation yang setara, dedahkan dua kekunci kedudukan: kedudukan nilai tiada dan kedudukan angka. Kedudukan pertama meletakkan kedudukan yang tiada di hujung. Apabila kedudukan pertama itu seri, nilai angka pengganti bagi kedudukan yang tiada tidak boleh bertembung dengan sifar yang hadir kerana item tersebut sudah mempunyai kedudukan yang berbeza.
Tambahkan sambungan dan pengiraan berikut selepas program lengkap di atas. Ini contoh bergantung yang menggunakan takrif ShapeItem, items dan expectedIDs yang sedia ada. Ia memerlukan macOS 14 atau lebih baharu kerana contoh ini membina ComparableComparator secara jelas dengan init(order:). Ketersediaan pemula
import Foundation
extension ShapeItem {
var missingPositionRank: Int {
effectivePosition == nil ? 1 : 0
}
var numericPositionKey: Int {
effectivePosition ?? 0
}
}
if #available(macOS 14.0, *) {
let comparators: [KeyPathComparator<ShapeItem>] = [
KeyPathComparator(\ShapeItem.group, order: .forward),
KeyPathComparator(\ShapeItem.missingPositionRank,
order: .forward),
KeyPathComparator(\ShapeItem.numericPositionKey,
order: .forward),
KeyPathComparator(
\ShapeItem.title,
comparator: ComparableComparator<String>(order: .forward)
),
KeyPathComparator(\ShapeItem.id, order: .forward)
]
let foundationOrdered = items.sorted(using: comparators)
precondition(foundationOrdered.map(\.id) == expectedIDs)
print(foundationOrdered.map(\.id))
} else {
print(items.sorted(by: comesBefore).map(\.id))
}
Pembanding tajuk yang dinyatakan secara jelas mengekalkan pilihan terdahulu, iaitu susunan rentetan Swift. Foundation mendokumenkan bahawa ComparableComparator menggunakan pelaksanaan Comparable bagi jenis yang dibandingkan, dan KeyPathComparator menerima pembanding sifat yang dibekalkan. ComparableComparator, Pemula pembanding laluan kekunci
Untuk satu susunan tetap, penutupan menjadikan setiap cabang jelas. Tatasusunan pembanding berguna apabila anda mahu susunan kekunci yang disokong diwakili sebagai nilai. Dalam kedua-dua pendekatan, kekalkan pemilihan nilai sandaran dalam sifat terhitung model supaya kedua-dua pelaksanaan menggunakan takrif yang sama.
Sesuaikan peraturan tanpa mengubah maksudnya
Untuk kedudukan menurun sambil mengekalkan kedudukan yang tiada di hujung, ubah hanya perbandingan antara dua kedudukan yang ada kepada left > right. Dalam versi Foundation, songsangkan hanya numericPositionKey; biarkan missingPositionRank mengikut susunan asal. Membalikkan keseluruhan hasil juga akan membalikkan kumpulan, tajuk, pengecam dan kedudukan nilai yang tiada.
Jika rentetan pilihan menjadi kekunci sandaran, tentukan secara berasingan sama ada rentetan kosong dikira sebagai tiada. Nil-Gabungan bertindak balas terhadap nil; ia tidak menggantikan rentetan kosong yang hadir. Tentukan sebarang peraturan penyeragaman tambahan sebelum mengisih, dan gunakan kekunci efektif yang sama secara konsisten. Tingkah laku nil-gabungan
Pastikan pengiraan kecil ini berjalan secara segerak. Panduan konkurensi Swift membezakan pengisihan segerak biasa daripada titik penggantungan, dan predikat sorted(by:) yang didokumenkan adalah segerak. Jika aplikasi memerlukan nilai daripada kerja tak segerak, dapatkan nilai itu dahulu sebelum memanggil pengisih ini. Menambah await di dalam penutupan perbandingan bukan lanjutan yang disokong bagi tandatangannya. Konkurensi, Tandatangan sorted(by:)
Sebelum menyesuaikan contoh ini untuk senarai desktop lain, kekalkan pemeriksaan bagi kekunci pilihan yang tiada, dua kekunci kedudukan yang tiada, nilai sifar yang sah, kedudukan efektif yang sama dan medan paparan yang semuanya seri. Tentukan sama ada seri terakhir patut mengekalkan susunan input atau menggunakan pengecam. Pilihan ini memberikan kontrak susunan yang tepat dan mudah difahami dalam kedua-dua pelaksanaan.
Sumber
- sorted(by:) — Apple
- Operator Asas — Projek Swift
- KeyPathComparator — Apple
- sorted(using:) — Apple
- ComparableComparator — Apple
- init(order:) — Apple
- init(_:comparator:) — Apple
- Konkurensi — Projek Swift