Swift Digest
SE-0372 | Swift Evolution

Document Sorting as Stable

Proposal
SE-0372
Authors
Nate Cook
Review Manager
Tony Allevato
Status
Implemented (Swift 5.8)

01 何が問題だったのか

stable sort とは、比較結果が等しい(または順序付けできない)要素同士の相対順序を、ソート前と同じまま保つソートのことです。たとえば姓でソート済みのプレイヤー一覧を、今度は名でソートしても、同じ名のプレイヤーは元の姓の順序のまま残ります。

var roster = [
   Player(first: "Sam", last: "Coffey"),
   Player(first: "Ashley", last: "Hatch"),
   Player(first: "Kristie", last: "Mewis"),
   Player(first: "Ashley", last: "Sanchez"),
   Player(first: "Sophia", last: "Smith"),
]

roster.sort(by: { $0.first < $1.first })
// roster == [
//    Player(first: "Ashley", last: "Hatch"),
//    Player(first: "Ashley", last: "Sanchez"),
//    Player(first: "Kristie", last: "Mewis"),
//    Player(first: "Sam", last: "Coffey"),
//    Player(first: "Sophia", last: "Smith"),
// ]

整数の配列のように Comparable 適合だけでソートする場合、等しい要素は見分けが付かないので stability は観察できません。しかし、要素のプロパティの一部だけを使ってソートする場合には、stability の有無が結果に表れます。スプレッドシートで複数の列を順にソートして多段ソートを実現するような使い方は、stability を前提としています。

標準ライブラリの sort() は Swift 5 より前のタイミングで stable な実装に変更されていました。しかし、ドキュメント上は次のように「stable とは保証しない」と明記されたままでした。

The sorting algorithm is not guaranteed to be stable. A stable sort preserves the relative order of elements that compare as equal.

この状態は、stability を知っている開発者にとっては「実際には stable なのに頼ってよいか分からない」という不便さにつながり、stability を知らない開発者にとっては「将来 stable でなくなったら原因不明のバグを踏みかねない」というリスクにつながっていました。

02 どのように解決されるのか

標準ライブラリの sort / sorted が stable であることを、ドキュメント上も正式に保証します。現行の Swift ランタイムはすべて stable な実装を含んでいる(ABI stability より前に stable 化されている)ため、API やアルゴリズムの変更は不要で、ドキュメントのコメントを次のように書き換えるだけの変更です。

- /// The sorting algorithm is not guaranteed to be stable. A stable sort
+ /// The sorting algorithm is guaranteed to be stable. A stable sort
  /// preserves the relative order of elements that compare as equal.

これにより、比較結果が等しい要素の相対順序が保たれることを前提にコードを書けるようになります。複数のキーで順次ソートして多段ソートを表現するパターンも、言語仕様として安心して使えます。今後 sort のアルゴリズムに変更が入る場合も、stability を維持することが要件となります。

Future Directions(参考)

今回のスコープ外ですが、今後の方向性としては、key path や関数ベースのソート API、ソート済みコレクション型/プロトコル、sort descriptor などの拡張が議論されています。また、stability を犠牲にする代わりに追加のメモリ確保を避けるといった別の特性を持つソート(unstableSort() 等)についても、必要になればその時点で命名やドキュメントで stability を明示する形で検討される見込みです。いずれも speculative な方向性で、実現が約束されているものではありません。