Yerel bir Swift dizisini birkaç alana göre sıralamak için her anahtarı öncelik sırasıyla karşılaştırın ve yalnızca eşitlik durumunda sonraki anahtara geçin. İsteğe bağlı yedek değerleri ayrı ayrı belirleyin, ardından eksik değerlerin nereye yerleşeceğini tanımlayın. Bu örnek, Mac masaüstü listesi için sentetik şekil etiketlerini sıralar.
Kapanışı yazmadan önce sıralama sözleşmesini tanımlayın
Bir masaüstü listesinin grup numarası, isteğe bağlı tercih edilen konumu, isteğe bağlı yedek konumu ve başlığı olan şekil etiketleri içerdiğini varsayalım. Amaç, bellekteki bu değerlerden öngörülebilir tek bir görüntüleme sırası oluşturmaktır.
İki karar için ayrı kurallar gerekir. Yedek seçimi, bir öğeyi hangi değerin temsil edeceğini belirler: varsa tercih edilen konumunu, yoksa yedek konumunu kullanın. Eşitlik bozucu, yalnızca iki öğenin etkin konumları eşit olduğunda başka bir alanı karşılaştırır. Swift’in nil birleştirme işleci ??, sarılmış değeri veya yedeği seçerek ilk kararı destekler. Temel İşleçler — Nil Birleştirme İşleci
Örnek için şu açık sözleşmeyi kullanın:
| Öncelik | Anahtar | Kural |
|---|---|---|
| 1 | Grup | Küçük sayılar önce gelir |
| 2 | Etkin konum | preferredPosition ?? backupPosition |
| 3 | Eksik konumların yeri | Her grupta konumu eksik olanlar, konumu bulunanlardan sonra gelir |
| 4 | Başlık | Konumlar eşitse Swift’in artan dize karşılaştırması |
| 5 | Tanımlayıcı | Önceki tüm anahtarlar eşitse küçük tanımlayıcılar önce gelir |
Eksik konumların sıralamadaki yeri, grubu geçersiz kılan bağımsız bir alan olarak değil, konum karşılaştırmasının parçası olarak belirlenir. Grup sıfırdaki bir öğe, konumu olmasa bile grup birdeki bir öğeden önce gelir.
Başlık karşılaştırması, bu sentetik hesaplama için bilerek Swift’in olağan dize sıralamasını kullanır. Kapanışı daha sonra Foundation karşılaştırıcılarıyla değiştirirseniz bu tercihi tutarlı biçimde koruyun.
Açık nil işleme ile birden çok alanı sıralayın
Aşağıdaki eksiksiz program Swift standart kitaplığını kullanır ve içe aktarma gerektirmez. Mac’inizde Swift destekli bir ortamda çalıştırın. Tüm girdiler programda tanımlanmıştır; hesaplama harici girdi veya kalıcı depolama kullanmaz. Sıralama işlemi sorted(by:) aracılığıyla yeni bir dizi döndürür. Apple’ın sorted(by:) belgeleri
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))
Beklenen çıktı:
[50, 70, 20, 40, 60, 10, 30]
Program ve beklenen sıra incelendi, ancak araştırma ortamında Swift derleyicisi bulunmadığından burada çalıştırılmadı. Programdaki önkoşullar, yerel olarak çalıştırdığınızda sonucu denetlemenizi sağlar.
Örnek öğelerin konumlarının nedeni
50 öğesi ilk sıradadır, çünkü konum değerlendirilmeden önce grup sıfır öncelik kazanır. Ardından 70 öğesi, sıfırın geçerli bir tercih edilen konum olduğunu gösterir: sekiz olan yedek değeri kullanılmaz. 20 öğesi, bir olan tercih edilen konumunu kullanır.
40, 60 ve 10 öğelerinin tümünün etkin konumu ikidir. İki Arc başlığı Circle başlığından önce gelir; tanımlayıcılar da 40 ve 60 arasındaki eşitliği bozar. Grup birdeki 30 öğesi son sıraya gelir, çünkü her iki konum alanı da eksiktir.
Bu, yaygın bir yedek değeri sorusunu da yanıtlar: tercih edilen konumu başka bir öğeyle eşit olduğunda 40 öğesi sıfır olan yedek konumunu kullanmaz. Bu sözleşmede yedek değer yalnızca tercih edilen değer yoksa onun yerini alır. Yedek değer eşitlikleri de bozacaksa ek bir karşılaştırma kuralı gerekir.
Değerler eşit olduğunda karşılaştırıcının geçerli kalmasını sağlayın
Swift, sıralama koşulunun katı zayıf sıralama oluşturmasını gerektirir. Pratikte bir öğe kendisinden önce gelmemeli, sıralama kararları karşılaştırmalar arasında tutarlı kalmalı ve eşdeğer öğeler tutarlı gruplar oluşturmalıdır. Apple bu gereklilikleri sorted(by:) için belgeliyor. Koşul gereklilikleri
Örnek, sıralama sonucunu döndürmeden önce önceki anahtarların farklı olup olmadığını denetler. Grup ve etkin konum eşit olduğunda başlık karşılaştırmasına geçer; tanımlayıcıya ancak başlık da eşitse geçer. Tanımlayıcılar ve önceki anahtarların tümü eşitse son katı karşılaştırma false döndürür.
Bağımsız alan karşılaştırmalarını || ile birleştirmekten kaçının. Bir öğenin grubu daha önce, konumu ise daha sonra gelebilir; diğer öğede bunun tersi olabilir. OR tabanlı bir koşul, her iki öğenin de diğerinden önce gelmesi gerektiğini söyleyebilir. Eşitliklerin sırayla ele alınması bu çelişkiyi önler.
Son karşılaştırma olarak <= kullanmaktan da kaçının: eşit değerler true döndürerek bir öğenin kendisinden önce gelemeyeceği kuralını ihlal eder. Her karşılaştırmayı yalnızca sağlanan değerlere ve sabit sıralama kurallarına bağlı tutun.
Eşitliklerde kararlı sıralama ile son tanımlayıcı arasında seçim yapın
Swift’in belgelenen sıralaması kararlıdır: koşulun eşdeğer bıraktığı öğeler göreli giriş sıralarını korur. İstenen davranış buysa son tanımlayıcı karşılaştırmasını kaldırın ve başlıklar eşit olduğunda false döndürün. Kararlı sıralama garantisi
Bu örnek bunun yerine kalan eşitlikleri çözmek için benzersiz sentetik tanımlayıcılar kullanır. Bu nedenle örnek girdinin sırasını tersine çevirmek yine aynı tanımlayıcı dizisini üretir. Bu özellik sözleşmeden ve örnek değerlerden kaynaklanır. Diğer tüm anahtarlarla birlikte tanımlayıcılar da tekrarlanırsa bu öğeler eşdeğer kalır ve giriş sıralarını korur.
Aynı kuralları Foundation anahtar yolu karşılaştırıcılarıyla ifade edin
Foundation, özellikleri karşılaştırmak için KeyPathComparator, bir dizi karşılaştırıcıyı uygulamak içinse sorted(using:) sağlar. İlk karşılaştırıcı birincil sıralamayı belirler; sonraki karşılaştırıcılar eşitlikleri çözer. Bu API’ler macOS 12’den itibaren kullanılabilir. KeyPathComparator, sorted(using:)
Eşdeğer bir Foundation sürümünde iki konum anahtarı tanımlayın: eksik değer sıralama derecesi ve sayısal konum. Derece, eksik konumları sona yerleştirir. Bu derece eşitse eksik değerler için kullanılacak sayısal bir değer, mevcut sıfırla çakışamaz; çünkü bu öğelerin dereceleri zaten farklıdır.
Aşağıdaki uzantıyı ve hesaplamayı, yukarıdaki eksiksiz programın sonuna ekleyin. Bu, programdaki mevcut ShapeItem, items ve expectedIDs tanımlarını kullanan bağımlı bir örnektir. ComparableComparator öğesini açıkça init(order:) ile oluşturduğundan macOS 14 veya sonraki bir sürümü gerektirir. Başlatıcının kullanılabilirliği
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))
}
Açık başlık karşılaştırıcısı, Swift dizesi sıralamasına ilişkin önceki tercihi korur. Foundation, ComparableComparator öğesinin karşılaştırılan türün Comparable uygulamasını kullandığını belgeler; KeyPathComparator ise sağlanan bir özellik karşılaştırıcısını kabul eder. ComparableComparator, Anahtar yolu karşılaştırıcısı başlatıcısı
Sabit bir düzen için kapanış, her dalı görünür kılar. Desteklenen anahtarların sırasını değerler olarak temsil etmek istediğinizde karşılaştırıcı dizisi kullanışlıdır. Her iki yaklaşımda da yedek seçimini modelin hesaplanan özelliğinde tutun; böylece her iki uygulama da aynı tanımı kullanır.
Anlamını değiştirmeden kuralları uyarlayın
Eksik konumları sonda tutarken konumları azalan sıraya göre sıralamak için yalnızca mevcut iki konum arasındaki karşılaştırmayı left > right olarak değiştirin. Foundation sürümünde yalnızca numericPositionKey öğesini tersine çevirin; missingPositionRank ileri yönde kalsın. Sonucun tamamını tersine çevirmek grupları, başlıkları, tanımlayıcıları ve eksik değerlerin sıralamadaki yerini de tersine çevirir.
İsteğe bağlı bir dize yedek anahtara dönüşürse boş dizenin eksik sayılıp sayılmayacağına ayrıca karar verin. Nil birleştirme nil durumuna tepki verir; mevcut boş bir dizenin yerini almaz. Sıralamadan önce ek normalleştirme kurallarını belirleyin ve aynı etkin anahtarı her yerde kullanın. Nil birleştirme davranışı
Bu küçük hesaplamayı eşzamanlı tutun. Swift’in eşzamanlılık kılavuzu, olağan eşzamanlı sıralamayı askıya alma noktalarından ayırır ve belgelenmiş sorted(by:) koşulu eşzamanlıdır. Bir uygulamanın eşzamansız işlerden gelen değerlere ihtiyacı varsa bu değerleri sıralayıcıyı çağırmadan önce alın. Karşılaştırma kapanışına await eklemek, işlev imzasının desteklenen bir uzantısı değildir. Eşzamanlılık, sorted(by:) imzası
Örneği başka bir masaüstü listesine uyarlamadan önce tercih edilen anahtarın eksik olması, her iki konum anahtarının da eksik olması, geçerli bir sıfır, eşit etkin konumlar ve tüm görüntüleme alanlarının eşit olması durumlarını denetlemeye devam edin. Son eşitliğin giriş sırasını mı koruyacağına yoksa tanımlayıcı mı kullanacağına karar verin. Bu seçimler, her iki uygulamada da anlaşılır kalan kesin bir sıralama sözleşmesi sağlar.
Kaynaklar
- sorted(by:) — Apple
- Temel İşleçler — Swift Project
- KeyPathComparator — Apple
- sorted(using:) — Apple
- ComparableComparator — Apple
- init(order:) — Apple
- init(_:comparator:) — Apple
- Eşzamanlılık — Swift Project