HOWTO · Rust
Rust でベクトルをソートする
sort_by() および sort() 関数は、ベクトルへの参照を受け取り、それをソートして返します。 デフォルトでは、値は昇順でソートされます。
このページの内容
Rustでベクターを昇順に並べ替えるには、可変のベクターに対して sort() を呼び出します。ベクター自体がその場で並べ替えられ、要素の自然な順序が使われます。特定のフィールドを比較したい場合や、別の順序を定義したい場合は sort_by() を使います。どちらもRust標準ライブラリで提供されています。
sort() でその場で並べ替える
要素の型に自然な全順序がある場合は、sort() が直接的な選択肢です。並べ替えによって要素の順序が変わるため、ベクターは可変でなければなりません。また、要素の型は Ord トレイトを実装している必要があります。整数や文字列は実装していますが、浮動小数点数型は実装していません。
次の例を実行します。
fn main() {
let mut numbers = vec![5, 3, 8, 1, 2];
numbers.sort();
println!("{:?}", numbers);
}
呼び出し後、numbers には同じ値が昇順で格納されています。このメソッドは新しいベクターを返すのではなく、既存のベクターを並べ替えます。そのため、結果を使う場合は可変の束縛が必要です。
文字列のベクターでも、sort() は型の自然な順序を使います。降順にしたい場合は、|a, b| b.cmp(a) のように比較の向きを逆にした sort_by() を使います。自然な順序がアプリケーションでの表示やグループ化の方法に合わない場合は、意図した順序を選んでください。
sort_by() で独自の順序を指定する
sort_by() は比較クロージャを受け取ります。Rustはクロージャに2つの要素を渡し、クロージャは Less、Equal、Greater のいずれかの Ordering 値を返します。これは各要素について1つのキーを返す関数ではなく、要素のペアを比較する仕組みです。Ord を実装する値では、cmp() メソッドを使うと結果を簡潔に返せます。
次の例では、タプルの2番目のフィールドを基準に、小さい順で並べ替えます。
fn main() {
let mut pairs = vec![(1, 3), (2, 1), (3, 2)];
pairs.sort_by(|a, b| a.1.cmp(&b.1));
println!("{:?}", pairs);
}
クロージャは a.1 と b.1 を比較しており、タプルの値自体は変更しません。複数のフィールドで並べ替えるには、まず優先するフィールドを比較し、同値の場合に別の比較で順序を決めます。比較クロージャは一貫性を保ち、同じ値のペアには安定した順序関係を示すようにしてください。sort_by() もベクターをその場で変更するため、可変の束縛が必要です。
浮動小数点数を安全に並べ替える
Rustの f32 型と f64 型は Ord ではなく PartialOrd を実装します。ほとんどの有限値は比較できますが、NaN を含む比較では順序が得られません。そのため、浮動小数点数のベクターに sort() を直接使うことはできず、値が NaN のときに partial_cmp() の結果をアンラップするとパニックする可能性があります。
浮動小数点数に対してRustの全順序を使う方針なら、比較クロージャで total_cmp() を使います。これにより NaN を含む順序が定まり、負のゼロと正のゼロも区別されます。次の自己完結した例には NaN が含まれているため、採用する方針が明確です。
fn main() {
let mut values = vec![3.5_f64, f64::NAN, -1.0, 2.0];
values.sort_by(f64::total_cmp);
println!("{:?}", values);
}
すべての浮動小数点数を1つの決定的な順序に含めたい場合は、total_cmp() が役立ちます。一方、アプリケーションで NaN を末尾にまとめる必要がある場合や、NaN を拒否する場合は、その方針を比較クロージャで明示するか、並べ替えの前にデータを検証してください。全順序は比較規則を定めるものであり、データにどの方針が適切かまでは決めません。