Array#sort
とそのコンパレータの動作を明確にするために、プログラミングコースの開始時に教えられたこの素朴な挿入ソートを検討してください。
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);
アルゴリズムとしての挿入ソートの選択を無視して、ハードコードされたコンパレータに焦点を合わせますarr[j-1] > arr[j]
。これには、議論に関連する2つの問題があります。
>
オペレータは、配列の要素がありますが、オブジェクトがに応答しないようにソートすることがあります多くのペアに呼び出された>
(私たちが使用した場合と同じが本当だろう合理的な方法で-
)。
- 数値を使用している場合でも、ここで焼き付けられている昇順の並べ替え以外の配置が必要になることがよくあります。
comparefn
よく知っている引数を追加することで、これらの問題を修正できます。
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));
これで、単純なソートルーチンが一般化されました。このコールバックがいつ呼び出されたかを正確に確認して、最初の一連の懸念に答えることができます。
配列ソートコールバック関数は、ソート中に何度も呼び出されますか?もしそうなら、毎回どの2つの数値が関数に渡されるのか知りたいです
以下のコードを実行すると、はい、関数が何度も呼び出され、console.log
どの番号が渡されたかを確認するために使用できることがわかります。
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))
);
あなたが尋ねる:
次に、2つの数値セットはどのように相互に関連してソートされますか?
用語を正確にa
及びb
ませんセット番号の- (あなたの例では、彼らしている数字)の配列ですばらしいスタイルオブジェクト。
真実は、実装に依存するため、どのようにソートされているかは問題ではありません。挿入ソートとは異なるソートアルゴリズムを使用した場合、コンパレータはおそらく異なる数値のペアで呼び出されますが、ソート呼び出しの最後に、JSプログラマーにとって重要な不変条件は、結果の配列が次のようにソートされることです。コンパレーターは、指定したコントラクトに準拠した値を返すと想定します(<0の場合a < b
、0の場合a === b
、> 0の場合a > b
)。
仕様に違反しない限り、ソートの実装を自由に変更できるのと同じ意味で、ECMAScriptの実装は、言語仕様の範囲内でソートの実装を自由に選択できるためArray#sort
、異なるコンパレータ呼び出しが生成される可能性があります。さまざまなエンジンで。ロジックが特定の比較シーケンスに依存しているコードを作成することはありません(また、コンパレータが最初に副作用を生成することもありません)。
たとえば、V8エンジン(執筆時点)は、配列が事前に計算された要素数よりも大きい場合にTimsortを呼び出し、小さな配列チャンクに対してバイナリ挿入ソートを使用します。ただし、以前は不安定なクイックソートを使用していたため、異なるシーケンスの引数とコンパレータへの呼び出しが行われる可能性がありました。
ソートの実装が異なれば、コンパレータ関数の戻り値の使用方法も異なるため、コンパレータがコントラクトに準拠していない場合、これにより予期しない動作が発生する可能性があります。例については、このスレッドを参照してください。