配列の要素をソートするためにマージソートをすると、要素を移動させるために配列のサイズに比例した追加のメモリが必要になります。 これはマージソートの重大な欠点と考えられます。 けれども、線形リストに対するマージソートにはこのような要求はありません。 C言語の相当する実装に時間効率と空間効率の両面で匹敵できるような、線形リストのマージソートを次に示します。 この実装にて捕捉された不変条件とその捕捉の容易さは、現実のプログラミングにおいて ATS が高い正確さを強制できるプログラミング言語であることを実証する大きな証拠です。
はじめに、ソートするリストの要素を比較する関数テンプレートの型定義とインターフェイスを導入しましょう:
// typedef cmp (a:t@ype) = (&a, &a) -> int // fun{a:t@ype} compare (x: &a, y: &a, cmp: cmp (a)): int //
fun{ a:t@ype } mergeSort{n:nat} (xs: list_vt (a, n), cmp: cmp a): list_vt (a, n) // end of [mergeSort]
2つのソート済みリストを1つにマージする関数テンプレートは次のように与えられます:
fun{ a:t@ype } merge{m,n:nat} .<m+n>. ( xs: list_vt (a, m), ys: list_vt (a, n) , res: &List_vt(a)? >> list_vt (a, m+n) , cmp: cmp a ) : void = case+ xs of | @list_vt_cons (x, xs1) => ( case+ ys of | @list_vt_cons (y, ys1) => let val sgn = compare<a> (x, y, cmp) in if sgn <= 0 then let // stable sorting val () = res := xs val xs1_ = xs1 prval () = fold@ (ys) val () = merge<a> (xs1_, ys, xs1, cmp) in fold@ (res) end else let val () = res := ys val ys1_ = ys1 prval () = fold@ (xs) val () = merge<a> (xs, ys1_, ys1, cmp) in fold@ (res) end // end of [if] end (* end of [list_vt_cons] *) | ~list_vt_nil () => (fold@ (xs); res := xs) ) // end of [list_vt_cons] | ~list_vt_nil () => (res := ys) // end of [merge]
次の関数テンプレートは与えられた線形リストを2つに分割します:
fun{ a:t@ype } split{n,k:nat | k <= n} .<n-k>. ( xs: &list_vt (a, n) >> list_vt (a, n-k), nk: int (n-k) ) : list_vt (a, k) = if nk > 0 then let val+@list_vt_cons(_, xs1) = xs val res = split<a> (xs1, nk-1); prval () = fold@(xs) in res end else let val res = xs; val () = xs := list_vt_nil () in res end // end of [if] // end of [split]
次の関数テンプレート msort は線形リストとその長さ、比較関数を取り、そして与えられた線形リストをソートしたものを返します:
fun{ a:t@ype } msort{n:nat} .<n>. ( xs: list_vt (a, n), n: int n, cmp: cmp(a) ) : list_vt (a, n) = if n >= 2 then let val n2 = half(n) val n3 = n - n2 var xs = xs // lvalue for [xs] val ys = split<a> (xs(*cbr*), n3) val xs = msort<a> (xs, n3, cmp) val ys = msort<a> (ys, n2, cmp) var res: List_vt (a) // uninitialized val () = merge<a> (xs, ys, res(*cbr*), cmp) in res end else xs // end of [msort]
最後に、mergeSort は msort 呼び出しを使って実装できます:
split と merge の実装を調べてみると、mergeSort が安定ソートになっていることがすぐにわかります。 つまり、ソートしている間に、等しい要素群の順序は保存されています。この章で紹介したコード全体と追加のテストコードは オンライン から入手できます。