プログラミング言語 Standard ML 入門 (問題の解答例)
10 モジュールシステム

10.5 functor文を使ったモジューラープログラミング

問 10.10

上記の QueueFUN を完成し,それを使って STQueueITQueue を定義せよ.

さらに,それらを定義の下で,BF ストラクチャおよび BFI ストラクチャを作成しなおし,

    BF.bf (fromPreOrder "1(2(3()())(4()()))(5()())");
    BFI.bf (fromIntPreOrder "1(2(3()())(4()()))(5()())");

などのテストプログラムを実行し,正しく動くことを確かめよ.

解答例  コード例を以下に示す。

   functor QueueFUN(type ty) :> POLY_QUEUE where type elem = ty =
   struct
     exception EmptyQueue
     type elem = ty
     type queue = elem list ref * elem list ref
     fun newQueue () = (ref [],ref []) : queue
     fun enqueue (i,(a,b))  = a := i :: (!a)
     fun dequeue (ref [],ref []) = raise EmptyQueue
       | dequeue (a as ref L, b as ref []) =
           let val (h::t) = rev L
           in (a:=nil; b:=t; h)
           end
       | dequeue (a,b as ref (h::t)) = (b := t; h)
   end
   structure ITQueue = QueueFUN(type ty = int tree)
   structure STQueue = QueueFUN(type ty = string tree)
   structure BF = struct
     structure Q = STQueue
     fun bf t =
       let val queue = Q.newQueue()
           fun loop () =
               (case Q.dequeue queue of
                      Node(data,r,l) => (Q.enqueue (r,queue);
                                         Q.enqueue (l,queue);
                                         data::loop())
                    | Empty => loop())
               handle Q.EmptyQueue => nil
       in (Q.enqueue (t,queue); loop())
       end
   end
   structure BFI = struct
     structure Q = ITQueue
     fun bf t =
       let val queue = Q.newQueue()
           fun loop () =
               (case Q.dequeue queue of
                      Node(data,l,r) => (Q.enqueue (l,queue);
                                         Q.enqueue (r,queue);
                                         data::loop())
                    | Empty => loop())
               handle Q.EmptyQueue => nil
       in (Q.enqueue (t,queue); loop())
       end
   end

以下は、SML#によるテスト例である。

   # val a = BF.bf (fromPreOrder "1(2(3()())(4()()))(5()())");
   val a = ["1", "2", "5", "3", "4"] : string list
   # val b = BFI.bf (fromIntPreOrder "1(2(3()())(4()()))(5()())");
   val b = [1, 2, 5, 3, 4] : int list
問 10.11

種々の要素の型に対する待ち行列を作成するもう1つの方法は, 待ち行列を ’a queue のような多相型にすることである. しかし多相型を持つ値に対する参照は許されない. そこで,参照型を使わずに,待ち行列のシグネチャを以下のように変更 することにする.

   signature FUNQUEUE = sig
      type ’a queue
      val newQueue : unit -> ’a queue
      val enqueue : ’a * ’a queue -> ’ a queue
      val dequeue : ’a queue -> ’a * ’a queue
   end

IntQueue および FastIntQueue を それぞれこのシグネチャを持つように変更し,汎用の待ち行列ストラクチャを作 成せよ.

解答例  以下にコード例を示す。このコードでは、FUNQUEUEシグネチャに EmptyQueue例外を追加してある。 また、汎用の待ち行列名をそれぞれQueueFastQueueとし 不透明なシグネチャ制約を付してある。

   signature FUNQUEUE = sig
     type ’a queue
     exception EmptyQueue
     val newQueue : unit -> ’a queue
     val enqueue : ’a * ’a queue -> ’a queue
     val dequeue : ’a queue -> ’a * ’a queue
   end

   structure Queue :> FUNQUEUE =
   struct
     exception EmptyQueue
     type ’a queue = ’a list
     fun newQueue() = nil : ’a queue
     fun enqueue (item,queue) = item :: queue
     fun dequeue nil = raise EmptyQueue
       | dequeue [x] = (x, nil)
       | dequeue (h::t) =
         let val (last, t’) =  dequeue t
         in (last, h::t’)
         end
   end

   structure FastQueue :> FUNQUEUE = struct
     exception EmptyQueue
     type ’a queue = ’a list * ’a list
     fun newQueue () = ([],[]) : ’a queue
     fun enqueue (i,(a,b))  = (i :: a, b)
     fun dequeue ([],[]) = raise EmptyQueue
       | dequeue (L, []) =
           let
               val (h::t) = rev L
           in
              (h, ([], t))
           end
      | dequeue (a,h::t) = (h, (a,t))
   end

以下は、SML#によるテスト例である。

   # val q = Queue.newQueue() : int Queue.queue;
   val q = _ : int Queue.queue
   # val q = Queue.enqueue(1, q);
   val q = _ : int Queue.queue
   # val q = Queue.enqueue(2, q);
   val q = _ : int Queue.queue
   # val q = Queue.enqueue(3, q);
   val q = _ : int Queue.queue
   # val (a,q) = Queue.dequeue q;
   val a = 1 : int
   val q = _ : int Queue.queue
   # val (b,q) = Queue.dequeue q;
   val b = 2 : int
   val q = _ : int Queue.queue
   # val (c,q) = Queue.dequeue q;
   val c = 3 : int
   val q = _ : int Queue.queue
   # val q’ = FastQueue.newQueue() : int FastQueue.queue;
   val q’ = _ : int FastQueue.queue
   # val q’ = FastQueue.enqueue(4, q’);
   val q’ = _ : int FastQueue.queue
   # val q’ = FastQueue.enqueue(5, q’);
   val q’ = _ : int FastQueue.queue
   # val q’ = FastQueue.enqueue(6, q’);
   val q’ = _ : int FastQueue.queue
   # val (d,q’) = FastQueue.dequeue q’;
   val d = 4 : int
   val q’ = _ : int FastQueue.queue
   # val (e,q’) = FastQueue.dequeue q’;
   val e = 5 : int
   val q’ = _ : int FastQueue.queue
   # val (f,q’) = FastQueue.dequeue q’;
   val f = 6 : int
   val q’ = _ : int FastQueue.queue
問 10.12

入出力バッファを生成するファンクタの定義を, FUNQUEUE シグネチャを持つストラクチャを受け取り BUFFER シグネチャを持つストラクチャを生成するように変更せよ.

解答例  以下にコード例を示す。

   functor BufferFUN(structure FQueue : FUNQUEUE )
           :> BUFFER =
   struct
     exception EndOfBuffer
     type channel = char FQueue.queue ref
     fun openBuffer () = ref (FQueue.newQueue()) : channel
     fun input ch =
        let
          val (out, queue) = (FQueue.dequeue (!ch))
        in
          (ch := queue; out)
        end
        handle FQueue.EmptyQueue => raise EndOfBuffer
     fun output(ch, c) = ch := FQueue.enqueue (c, !ch)
   end