about me

Twitter: @kinaba

15:08 09/04/28

Mergesort

「集合」を明示的に値とするアプローチ [1] [2] だと、「同順の要素の並びはどちらでもいい」ソートアルゴリズムは、 全ての可能なソート順の集合を返すことになると思います。

-- msort_one xs = one_of (msort xs) msort [] = {[]} msort xs = let (ys,zs) = split xs in {merge xs' ys' | xs'∈msort xs, ys'∈msort ys} where merge ys [] = {ys} merge [] zs = {zs} merge (y::ys) (z::zs) = if y<z then {y::ws | ws ∈ merge ys (z::zs)} else if z<y then {z::ws | ws ∈ merge (y::ys) zs} else {y::ws | ws ∈ merge ys (z::zs)} ∪ {z::ws | ws ∈ merge (y::ys) zs} split xs = 略

これ、本当に非決定性が気になる最後の分岐以外でも全ての場面で集合を気にしなくてはいけなくて、 書くの面倒じゃないでしょうか。自分は、せめてこう書きたい：

x |?| y = if cointoss then x else y msort [] = [] msort xs = let (ys,zs) = split xs in merge (msort xs) (msort ys) where merge ys [] = ys merge [] zs = zs merge (y::ys) (z::zs) = if y<z then y :: merge ys (z::zs) else if z<y then z :: merge (y::ys) zs else (y :: merge ys (z::zs)) |?| (z :: merge (y::ys) zs) split xs = 略

いや、これにも全く満足はできていないのですが。シングルトン集合 {e} はそのまま e、 内包表記による集合に一般化した関数適用もそのままただの関数適用、合併 ∪ は |?| になります。 一つだけを選び出す際の one_of は不要です。

空集合や、その他の集合演算（共通部分 ∩、可能な答え全部を取り出す all_of などなど…） を許すとバックトラック等が必要なヘビーウェイトな非決定性になってしまうので、 それは"この文脈では"おそらく考えるべきではないと思う。 （…というのが、もしかしたら私の主張の最大のポイント、ということになるのかもしれない。）

別の言い方をすると「++ と >>= と return だけを許す リストモナド にプログラム全体を閉じ込めてしまえ」という主張になります、たぶん。 扱う対象は、常に "複数の値を束ねた「集合」" です。 あまりにも当たり前に集合過ぎるのでソースコード上では見えないだけ。 Haskell で IO モナドから逃げられないのと同じで、このリストモナドからは逃げられません。 main :: List a に最後に one_of が（プログラムの世界の外から）適用されて実行が終わります。 「++ と >>= と return と one_of だけを許す」と制限することで、 (1) まず、仕様にあう処理系実装をゼロコストで実現するのは現時点でも非常に簡単 (2) さらに、何か画期的にわかりやすい構文で非決定性が表現できる気がしてならないのですが、 cointoss も |?| もなにか違うような、違和感がある、というのが現状です。