プログラミング言語 Standard ML 入門 (問題の解答例)
10.5 functor文を使ったモジューラープログラミング
問 10.10
上記の QueueFUN を完成し,それを使って STQueue と ITQueue を定義せよ.
さらに,それらを定義の下で,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例外を追加してある。 また、汎用の待ち行列名をそれぞれQueue、FastQueueとし 不透明なシグネチャ制約を付してある。
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