fetburner.core

コアダンプ

SMLでソート

この記事はML Advent Calendar 2014 8日目のために書かれました。

多くの言語においてソートは標準ライブラリで提供されていますが、Standard ML Basis Libraryにはソートを行う関数が存在しません。 処理系依存で良ければSML/NJのListMergeSort.sortの様に用意されている場合もある様ですが、 ここでは別の方法を考えたいと思います。

方法

処理系依存のライブラリを使いたくないのであれば自前で用意する事になりますが、 自分で実装するとなると信頼性を不安に思う事でしょう。

そのような場合にはCoqが便利です。 関数の満たすべき性質を証明することでテストでは得られない信頼性が手に入りますし、 Extractionという機能を用いる事でCoqのコードを欲しい言語のコードに変換する事ができます。 SMLのコードへの変換は標準でサポートされていませんが、非公式の改造を施されたCoqを用いればSMLのコードを出力する事ができるようになります。

従って、信頼性の高いコードが得たいのなら

  • Coqでコードを書く
  • 満たすべき性質を証明する
  • Extractionによって目的とする言語のコードを出力する

といった手順を辿れば良い事が分かります。 幸運な事にマージソートを行う関数がCoqの標準ライブラリで提供されているので、 ソートに関しては自分でコードを書いて性質を証明する必要はありません。

結果

Coqのコードとこれによって出力されるSMLのコードを以下に示します。

Require Import Mergesort.
Extraction Language Sml.
Extract Constant app => "(fn x => fn y => x @ y)". 
Extract Inductive option => "option" ["SOME" "NONE"].
Extract Inductive bool => bool ["true" "false"].
Extract Inductive list => "list" ["[]" "(::)"].
Extraction "mergesort.sml" Sort.
(** val app : 'a1 list -> 'a1 list -> 'a1 list **)

val app = (fn x => fn y => x @ y)

signature TotalLeBool' = 
 sig 
  type t 
  
  val leb : t -> t -> bool
 end

functor Sort
  (X:TotalLeBool')  = 
 struct 
  (** val merge : X.t list -> X.t list -> X.t list **)
  
  fun merge l1 l2 =
    let fun merge_aux l3 =
      case l1 of
        [] => l3
      | a1::l1' =>
        (case l3 of
           [] => l1
         | a2::l2' =>
           if X.leb a1 a2 then a1::(merge l1' l3) else a2::(merge_aux l2'))
    in merge_aux l2 end 
  
  (** val merge_list_to_stack :
      X.t list option list -> X.t list -> X.t list option list **)
  
  fun merge_list_to_stack stack l =
    case stack of
      [] => (SOME l)::[]
    | y::stack' =>
      (case y of
         SOME l' => NONE::(merge_list_to_stack stack' (merge l' l))
       | NONE => (SOME l)::stack')
  
  (** val merge_stack : X.t list option list -> X.t list **)
  
  fun merge_stack stack =
    case stack of
      [] => []
    | y::stack' =>
      (case y of
         SOME l => merge l (merge_stack stack')
       | NONE => merge_stack stack')
  
  (** val iter_merge : X.t list option list -> X.t list -> X.t list **)
  
  fun iter_merge stack l =
    case l of
      [] => merge_stack stack
    | a::l' => iter_merge (merge_list_to_stack stack (a::[])) l'
  
  (** val sort : X.t list -> X.t list **)
  
  val sort =
    iter_merge []
  
  (** val flatten_stack : X.t list option list -> X.t list **)
  
  fun flatten_stack stack =
    case stack of
      [] => []
    | o0::stack' =>
      (case o0 of
         SOME l => app l (flatten_stack stack')
       | NONE => flatten_stack stack')
 end

SML/NJの対話環境で実際に試してみるとこんな感じでしょうか。

- use "mergesort.sml";
[opening mergesort.sml]
val app = fn : 'a list -> 'a list -> 'a list
signature TotalLeBool' =
  sig
    type t
    val leb : t -> t -> bool
  end
functor Sort(X: sig
                  type t
                  val leb : t -> t -> bool
                end) :
            sig
              val merge : X.t list -> X.t list -> X.t list
              val merge_list_to_stack : X.t list option list
                                        -> X.t list -> X.t list option list
              val merge_stack : X.t list option list -> X.t list
              val iter_merge : X.t list option list -> X.t list -> X.t list
              val sort : X.t list -> X.t list
              val flatten_stack : 'a list option list -> 'a list
            end
val it = () : unit
- structure Int : TotalLeBool' =
= struct
=   type t = int
=   fun leb a b = a <= b
= end;
structure Int : TotalLeBool'
- structure IntSort = Sort (Int);
structure IntSort :
  sig
    val merge : X.t list -> X.t list -> X.t list
    val merge_list_to_stack : X.t list option list
                              -> X.t list -> X.t list option list
    val merge_stack : X.t list option list -> X.t list
    val iter_merge : X.t list option list -> X.t list -> X.t list
    val sort : X.t list -> X.t list
    val flatten_stack : 'a list option list -> 'a list
  end
- IntSort.sort [1, 1, 4, 5, 1, 4, 8, 10];
val it = [1,1,1,4,4,5,8,10] : Int.t list

最後に

今回はソートを扱いましたが、Coqの標準ライブラリではAVL木やヒープなど便利そうなプログラムが提供されているので一度目を通してはどうでしょうか。 ちょうど欲しかったプログラムが証明も付いて手に入るかもしれません。

参考

[1] "The Standard ML Basis Library", http://sml-family.org/Basis/

[2] "Standard Library | The Coq Proof Assistant", https://coq.inria.fr/library/

[3] "証明駆動開発入門(2)", http://www.iij-ii.co.jp/lab/techdoc/coqt/coqt9.html