練習問題 9.3 モジュールとシグネチャを使ったキューリスト

以下は、書き換え可能データを使わないキューのシグネチャです。add などは要素を追加した新しいキューを返すようになっています。

# module type QUEUE =
    sig

        type 'a t

        exception Empty

        val empty: 'a t

        val add: 'a t -> 'a -> 'a t

        val take: 'a t -> 'a * 'a t

        val peek: 'a t -> 'a

    end;;

module type QUEUE =
    sig
        type 'a t
        exception Empty
        val empty : 'a t
        val add : 'a t -> 'a -> 'a t
        val take : 'a t -> 'a * 'a t
        val peek : 'a t -> 'a
    end

このシグネチャを持つモジュールを以下に述べる二種類の実現方法で定義しなさい。
ひとつめのモジュール Queue1 は、キューをリストで表現します。例えば、[1; 5; 4; 3; 2] は、キュー 1, 5, 4, 3, 2 を表現します。もうひとつの Queue2 は、待ち行列をリストのペアで表現します(同じキューの状態を表現するリストのペアは複数あり得ますが、キューが空でない限り常に一つ目のリストが空でないようにしなさい)。このような表現により、add、take ともに(平均)定数時間で行えます。以下に、モジュール定義の一部を示すので、完成させた上で、動作確認をしなさい。

module Queue1 : QUEUE =
    struct

        type 'a t = 'a list
        
        ...

        let peek = function [] -> raise Empty | x :: rest -> x

    end

module Queue2 : QUEUE =
    struct
        type 'a t = Queue of ('a list * 'a list)

        ...

        let peek = function
            Queue ([], _) -> raise Empty
            | Queue (x :: _, _) -> x
    end

解答

module type QUEUE =
    sig

        type 'a t

        exception Empty

        val empty: 'a t

        val add: 'a t -> 'a -> 'a t

        (* 先頭要素と残りの組を返す *)
        val take: 'a t -> 'a * 'a t

        (* 先頭要素を返す *)
        val peek: 'a t -> 'a

    end;;

(* Queue1 *)

module Queue1 : QUEUE =
    struct

        type 'a t = 'a list

        exception Empty

        let empty = []
        
        let add l x = l @ (x :: [])

        let take = function
            [] -> raise Empty 
        | a :: rest -> (a, rest)

        let peek = function [] -> raise Empty | x :: rest -> x

    end;;

(* Queue2 *)

module Queue2 : QUEUE =
    struct
        type 'a t = Queue of ('a list * 'a list)

        exception Empty

        let empty = Queue ([], [])

        let add l x =
            match l with
            Queue ([], []) -> Queue ([x], [])
        | Queue (a, b) -> Queue (a, x :: b)

        let rec reverse = function
            [] -> []
        | v :: rest -> (reverse rest) @ (v :: [])

        let take = function
            Queue ([], _) -> raise Empty
        | Queue (x :: [], b) -> (x, Queue((reverse b), []))
        | Queue (x :: rest, b) -> (x, Queue(rest, b))

        let peek = function
            Queue ([], _) -> raise Empty
            | Queue (x :: _, _) -> x
    end;;

実行例 — Queue1

# let rec dump1 qlist1 =
    try
        let (v, rest) = Queue1.take qlist1 in
        v :: (dump1 rest)
    with Queue1.Empty -> [];;
val dump1 : 'a Queue1.t -> 'a list = <fun>

# let ( <<< ) l x = Queue1.add l x;;
val ( <<< ) : 'a Queue1.t -> 'a -> 'a Queue1.t = <fun>

# let mylist1 = Queue1.empty;;
val mylist1 : 'a Queue1.t = <abstr>

# let mylist1 = mylist1 <<< "sachiko" <<< "seiichi";;
val mylist1 : string Queue1.t = <abstr>

# dump1 mylist1;;
- : string list = ["sachiko"; "seiichi"]

# Queue1.peek mylist1;;
- : string = "sachiko"

実行例 — Queue2

# let rec dump2 qlist2 =
    try
        let (v, rest) = Queue2.take qlist2 in
        v :: (dump2 rest)
    with Queue2.Empty -> [];;
val dump2 : 'a Queue2.t -> 'a list = <fun>

# let ( <== ) l x = Queue2.add l x;;
val ( <== ) : 'a Queue2.t -> 'a -> 'a Queue2.t = <fun>

# let mylist2 = Queue2.empty;;
val mylist2 : 'a Queue2.t = <abstr>

# let mylist2 = mylist2 <== "sayuri" <== "seiichi";;
val mylist2 : string Queue2.t = <abstr>

# dump2 mylist2;;
- : string list = ["sayuri"; "seiichi"]

# Queue2.peek mylist2;;
- : string = "sayuri"

参考

実行例をつくるのに、以下のページを参考にさせていただきました。

gifnksmの雑多なメモ