14.1 Time と Timer ストラクチャ
問 14.1
日時の表現と操作は DATE シグネチャを持つ
Date ストラクチャで提供されている.
DATE シグネチャはその型から容易にその機能を推定できる.
必要なら Date ストラクチャを使い,現在の日時と時刻を表す文字列
を返す関数
currentTime : unit -> string
を定義せよ.
解答例 以下に定義と実行の例を示す。
# fun currentTime () = Date.toString (Date.fromTimeLocal (Time.now())); val currentTime = fn : unit -> string # currentTime (); val it = "Sun Jul 19 10:17:42 2020" : string
問 14.2
関数 nlogn : int -> real と intcomp : int*int -> order を定義 し,checkTime 関数を完成させよ.
解答例 以下に例を示す。 以下では、intcompは、Intストラクチャに定義されている compare関数を使用している。
fun log2 x = Math.log10 x / (Math.log10 2.0) fun nlogn n = ((Real.fromInt n) * (log2 (Real.fromInt n))) fun checkTime n = let val array = genArray n val tm = timeRun ArrayQuickSort.sort (array,Int.compare) val nlognRatio = tm / (nlogn n) in (n, tm div 1000, nlognRatio) end
問 14.3
関数 printResult : int * int * real -> unit は, の値,時間,時間ととの比の値を受け取り, size=10000, milli-secs=40, micro-secs/n log(n)=0.30103000 の形式でプリントする関数である. printResult 関数を定義し, checkTime 関数を完成させ, 前節で作成したソートプログラムの評価を行え.
解答例 文脈から、完成させるべき関数は、checkTimeではなく、testSort関数である。 ここでは、SML#の動的型付け機構を使って定義されている以下の型の汎用の 清書関数
# Dynamic.pp; val it = fn : [’a#reify. ’a -> unit]
を使った定義例を以下に示す。
fun printResult (n, tm, ratio) = Dynamic.pp {"size" = n, "milli-secs" = tm, "micro-secs/n long(n)" = ratio} fun testSort n = printResult (checkTime n)
以下は、SML#の対話型環境下での実行結果である。
# app testSort [1000, 10000, 1000000, 10000000]; {"micro-secs/n long(n)" = 0.0, "milli-secs" = 0, size = 1000} {"micro-secs/n long(n)" = 0.0602059991328, "milli-secs" = 8, size = 10000} {"micro-secs/n long(n)" = 0.0834856521308, "milli-secs" = 1664, size = 1000000} { "micro-secs/n long(n)" = 0.0822069913873, "milli-secs" = 19116, size = 10000000 } val it = () : unit
問 14.4
配列の大きさのリストを受け取り,各について の値,計算時間,の値との比を
- evalSort [10000,100000,1000000,10000000]; array size milli-sec. micro s./n log(n) 10000 30 0.22577250 100000 460 0.27694760 1000000 5740 0.28798536 10000000 66630 0.28653755 ------------------------------------------------------------ avarage 0.26931075
のような形式で表示する関数 evalSort を以下の手順で作成せよ.
-
1.
20文字以下の大きさの文字列を受け取り,その前に空白文字を 付け加え,20文字の大きさの文字列を返す関数 padString : string -> string を定義せよ.
-
2.
padString を使って,2つの整数と1つの実数を受け取り,以下のよう な形式でプリントする関数 printLine を定義せよ. - printLine; val it = fn : int * int * real -> unit - printLine (10000,30,0.22577250);
10000 30 0.22577250
val it = () : unit -
3.
以上を使って evalSort を定義せよ.
解答例 evalSortの定義例を以下に示す。 この例では、padStringを定義する代わりに、StringCvtストラクチャの padLeft関数を使用している。
fun evalSort L = let val L’ = map checkTime L val av = (foldr (fn ((_,_,x),y) => y+x) 0.0 L’)/(Real.fromInt (List.length L’)) val title = (StringCvt.padLeft #" " 20 "array size") ^ (StringCvt.padLeft #" " 20 "milli-sec.") ^ (StringCvt.padLeft #" " 20 "micro s./nlogn") ^ "\n" fun formatReal a = StringCvt.padLeft #" " 20 (Real.fmt (StringCvt.FIX (SOME 8)) a) fun printLine (n,a,c) = let val ns = StringCvt.padLeft #" " 20 (Int.toString n) val sa = StringCvt.padLeft #" " 20 (Int.toString a) val sc = formatReal c in print (ns ^ sa ^ sc ^ "\n") end in (print title; map printLine L’; print "------------------------------------------------------------\n"; print (" avarage" ^ (formatReal av)); print "\n") end
以下は、SML#の対話型環境下での実行結果である。 現在の実行環境では、大きさ10000の配列では短時間に終了し、誤差が大きくなるとため、 100000、1000000, 10000000、67108863(最大配列サイズ)のテスト行っている。
# evalSort [100000, 1000000, 10000000, Array.maxLen div 4]; array size milli-sec. micro s./nlogn 100000 152 0.09151312 1000000 1784 0.08950625 10000000 20300 0.08729870 67108863 144932 0.08306366 ------------------------------------------------------------ avarage 0.08784543 val it = () : unit
問 14.5
関数 checkTime を一般化し,汎用のテスト 関数
eval : {prog:’a->’b, input:’a, size:’a-> int, base:int -> real} -> int * real * real
を定義せよ. ただし,引数レコードの各フィールドの意味は以下のとおりとする.
フィールド | 値の意味 |
---|---|
prog | 評価すべきプログラム |
input | プログラムへの入力 |
size | 入力の大きさを返す関数 |
base | 評価の基準となる関数 |
結果の型の意味は checkTime の場合と同様である.
解答例 checkTimeと同様であれば、結果の型はint * int * realである。 以下は定義例である。
fun eval {prog, input, size, base} = let val tm = timeRun prog input val n = size input val ratio = Real.fromInt tm / base n in (n, tm div 1000, ratio) end
問 14.6
自然数のリストを受け取り,各要素に対して,大きさの乱数配列 を生成し,上記のような回の比較を行い,処理時間およびそのとの比を
- evalCompare [500000,1000000,5000000]; array size milli-sec. micro s./n 500000 40 0.08000000 1000000 70 0.07000000 5000000 370 0.07400000 ------------------------------------------------------------ avarage 0.07466667
のような形式で表示する関数 evalCompare を,eval を使って定義せよ.
解答例 以下に定義例を示す。
fun evalCompare L = let fun comp array = let val n = Array.length array val p = Array.sub(array, 0) val m = n div 2 fun loop x = if x <= m then () else (Int.compare (Array.sub(array, n - x), p); Int.compare (Array.sub(array, x - 1), p); loop (x -1)) in loop n end fun evalN n = eval {prog = comp, input = genArray n, size = Array.length, base = real} val L’ = map evalN L val av = (foldr (fn ((_,_,x),y) => y+x) 0.0 L’)/(Real.fromInt (List.length L’)) val title = (StringCvt.padLeft #" " 20 "array size") ^ (StringCvt.padLeft #" " 20 "milli-sec.") ^ (StringCvt.padLeft #" " 20 "micro s./n") ^ "\n" fun formatReal a = StringCvt.padLeft #" " 20 (Real.fmt (StringCvt.FIX (SOME 8)) a) fun printLine (n,a,c) = let val ns = StringCvt.padLeft #" " 20 (Int.toString n) val sa = StringCvt.padLeft #" " 20 (Int.toString a) val sc = formatReal c in print (ns ^ sa ^ sc ^ "\n") end in (print title; map printLine L’; print "------------------------------------------------------------\n"; print (" avarage" ^ (formatReal av)); print "\n") end
以下にSML#でのテスト例を示す。
# evalCompare [1000000, 10000000, Array.maxLen div 4]; array size milli-sec. micro s./n 1000000 8 0.00800000 10000000 80 0.00800000 67108863 564 0.00840426 ------------------------------------------------------------ avarage 0.00813475 val it = () : unit
問 14.7
evalSort と evalCompare を組み合わせ,ソート関数の処理性能を
- normalEvalSort [500000,1000000,5000000,10000000]; array size time in cunit T/n log(n) 500000 2740 3.75926754 1000000 5860 3.81825925 5000000 33270 3.88323623 10000000 68970 3.85195525 ------------------------------------------------------------ avarage 3.82817957 The estimated sort time function: T(n) = 3.8 n log(n).
のような形式で表示をする関数 normalEvalSort を定義せよ.
解答例 以下に定義例を示す。
fun evalCompare L = let fun comp array = let val n = Array.length array val p = Array.sub(array, 0) val m = n div 2 fun loop x = if x <= m then () else (Int.compare (Array.sub(array, n - x), p); Int.compare (Array.sub(array, x - 1), p); loop (x -1)) in loop n end fun evalN n = let val array = genArray n in eval {prog = comp, input = array, size = Array.length, base = real} end val L’ = map evalN L val av = (foldr (fn ((_,_,x),y) => y+x) 0.0 L’)/(Real.fromInt (List.length L’)) in av end fun evalSortN c L = let fun sort array = ArrayQuickSort.sort (array, Int.compare) fun base n = c * nlogn n fun evalN n = eval {prog = sort, input = genArray n, size = Array.length, base = base} val L’ = map evalN L val av = (foldr (fn ((_,_,x),y) => y+x) 0.0 L’) / (Real.fromInt (List.length L’)) val title = (StringCvt.padLeft #" " 20 "array size") ^ (StringCvt.padLeft #" " 20 "time in cunit") ^ (StringCvt.padLeft #" " 20 "C/nlogn") ^ "\n" fun formatReal a = StringCvt.padLeft #" " 20 (Real.fmt (StringCvt.FIX (SOME 8)) a) fun printLine (n,a,c) = let val ns = StringCvt.padLeft #" " 20 (Int.toString n) val sa = StringCvt.padLeft #" " 20 (Int.toString (Real.floor (real a * 1000.0/ c))) val sc = formatReal c in print (ns ^ sa ^ sc ^ "\n") end val C = Real.fmt (StringCvt.FIX (SOME 2)) av in (print title; map printLine L’; print "------------------------------------------------------------\n"; print (" avarage" ^ (formatReal av) ^ "\n"); print ("The estimated sort time function: T(n) = " ^ C ^ " n log(n)\n")) end fun normalEvalSort L = evalSortN (evalCompare L) L
以下にSML#でのテスト例を示す。
# normalEvalSort [1000000, 10000000, Array.maxLen div 4]; array size time in cunit C/nlogn 1000000 236268 7.43223684 10000000 2756460 7.62717282 67108863 20683151 7.75259017 ------------------------------------------------------------ avarage 7.60399994 The estimated sort time function: T(n) = 7.60 n log(n) val it = () : unit