Davranışını Array#sort
ve karşılaştırıcısını açıklığa kavuşturmaya yardımcı olmak için , başlangıç programlama kurslarında öğretilen bu saf ekleme sıralamasını düşünün :
const sort = arr => {
for (let i = 1; i < arr.length; i++) {
for (let j = i; j && arr[j-1] > arr[j]; j--) {
[arr[j], arr[j-1]] = [arr[j-1], arr[j]];
}
}
};
const array = [3, 0, 4, 5, 2, 2, 2, 1, 2, 2, 0];
sort(array);
console.log("" + array);
Üzerinde algoritma, odak noktası olarak ekleme tür seçim yok sayılması kodlanmış karşılaştırıcı: arr[j-1] > arr[j]
. Bunun tartışmayla ilgili iki sorunu vardır:
>
Operatör dizi öğelerinin ancak nesneler yanıt vermeyen gibi sıralamak isteyebilirsiniz birçok şeyi çiftleri üzerinde çağrılır >
(biz kullanıldığında aynı doğru olurdu makul bir şekilde -
).
- Sayılarla çalışıyor olsanız bile, çoğu zaman burada pişirilen yükselen türden başka bir düzenleme istersiniz.
comparefn
Aşina olduğunuz bir argüman ekleyerek bu sorunları çözebiliriz :
const sort = (arr, comparefn) => {
for (let i = 1; i < arr.length; i++) {
for (let j = i; j && comparefn(arr[j-1], arr[j]) > 0; j--) {
[arr[j], arr[j-1]] = [arr[j-1], arr[j]];
}
}
};
const array = [3, 0, 4, 5, 2, 2, 2, 1, 2, 2, 0];
sort(array, (a, b) => a - b);
console.log("" + array);
sort(array, (a, b) => b - a);
console.log("" + array);
const objArray = [{id: "c"}, {id: "a"}, {id: "d"}, {id: "b"}];
sort(objArray, (a, b) => a.id.localeCompare(b.id));
console.log(JSON.stringify(objArray, null, 2));
Şimdi saf sıralama rutini genelleştirildi. İlk endişelerinizi yanıtlayarak bu geri aramanın tam olarak ne zaman çağrıldığını görebilirsiniz:
Dizi sıralaması geri çağrı işlevi, sıralama sırasında birçok kez çağrılır mı? Öyleyse, her seferinde işleve hangi iki sayının aktarıldığını bilmek istiyorum
Aşağıdaki kodu çalıştırmak, evet, işlevin birçok kez çağrıldığını ve console.log
hangi numaraların aktarıldığını görmek için kullanabilirsiniz :
const sort = (arr, comparefn) => {
for (let i = 1; i < arr.length; i++) {
for (let j = i; j && comparefn(arr[j-1], arr[j]) > 0; j--) {
[arr[j], arr[j-1]] = [arr[j-1], arr[j]];
}
}
};
console.log("on our version:");
const array = [3, 0, 4, 5];
sort(array, (a, b) => console.log(a, b) || (a - b));
console.log("" + array);
console.log("on the builtin:");
console.log("" +
[3, 0, 4, 5].sort((a, b) => console.log(a, b) || (a - b))
);
Sen sor:
İki sayı kümesi daha sonra birbirlerine göre nasıl sıralanır?
Terminoloji konusunda kesin olmak gerekirse a
ve sayı kümelerib
değildir - bunlar dizideki nesnelerdir (örneğinizde sayılardır).
Gerçek şu ki, nasıl sıralandıkları önemli değil çünkü uygulamaya bağlı. Eklemeli sıralamadan farklı bir sıralama algoritması kullansaydım, karşılaştırıcı muhtemelen farklı sayı çiftlerinde çalıştırılırdı, ancak sıralama çağrısının sonunda JS programcısı için önemli olan değişmez, sonuç dizisinin şuna göre sıralanmasıdır. karşılaştırıcı, karşılaştırıcının belirttiğiniz sözleşmeye uyan değerler döndürdüğünü varsayarak (<0 ne zaman a < b
, 0 ne zaman a === b
ve> 0 ne zaman a > b
).
Aynı anlamda, benim spesifikasyonumu ihlal etmediğim sürece türümün uygulamasını değiştirme özgürlüğüne sahip olduğum gibi, ECMAScript uygulamaları, dil spesifikasyonu sınırları içinde sıralama uygulamasını seçmekte özgürdür , bu nedenle Array#sort
muhtemelen farklı karşılaştırıcı çağrılar üretecektir. farklı motorlarda. Mantığın belirli bir karşılaştırma dizisine dayandığı durumlarda kod yazılmaz (ya da karşılaştırıcı ilk etapta yan etkiler üretmemelidir).
Örneğin, V8 motoru (yazma sırasında) dizi önceden hesaplanan bazı öğelerden daha büyük olduğunda Timsort'u çağırır ve küçük dizi parçaları için ikili bir ekleme sıralaması kullanır . Bununla birlikte, kararsız olan ve muhtemelen karşılaştırıcıya farklı bir argüman ve çağrı dizisi verecek olan hızlı sıralama kullanırdı .
Farklı sıralama uygulamaları, karşılaştırıcı işlevinin dönüş değerini farklı şekilde kullandığından, karşılaştırıcı sözleşmeye uymadığında bu şaşırtıcı davranışlara yol açabilir. Bir örnek için bu konuya bakın .