プログラミング言語 Standard ML 入門 (問題の解答例)
16.3 書式付き書き出し処理
問 16.7
上で定義した formatData 関数を使って, string * string * string 型データと int * int * int list 型の データを受け取り,
- printData ("first", "second", "third") [(1,2,3),(4,5,6)];
first second third
1 2 3
4 5 6
のような形式でプリントする関数 printTriple を書け.
解答例
fun printTriple (s1, s2, s3) L =
let
val width = 10
fun prS s =
print
(formatData
{kind=STRING, width=SOME width, align=RIGHT} (S s))
fun prI i =
print
(formatData
{kind=INT StringCvt.DEC, width=SOME width, align=RIGHT} (I i))
fun printLine (i1,i2,i3) = (prI i1; prI i2; prI i3; print "\n")
in
prS s1;
prS s2;
prS s3;
print "\n";
map printLine L
end
問 16.8
第16.2節で定義した intScan を利用し, 与えられた部分文字列の先頭が整数の表現であれば SOME と残りの 部分文字列を返し,数字表現でなければ NONE と与えられた部分文字列その ものを返す関数
scanInt : substring -> int option * substring
を書け.
解答例
fun scanInt s =
case intScan s of
SOME (i,s) => (SOME i, s)
| NONE => (NONE, s)
問 16.9
以上の関数定義をまとめて,以下のシグネチャを持つストラクチャFormat を構築せよ.
signature FORMAT =
sig datatype kind = INT of StringCvt.radix
| REAL of StringCvt.realfmt
| STRING
| BOOL
datatype align = LEFT | RIGHT
datatype format =
LITERAL of string
| SPEC of {kind:kind,width:int option,align:align}
datatype argument = I of int
| R of real
| S of string
| B of bool
exception formatError
val format : string -> argument list -> string
val printf : string -> argument list -> unit
end
ただし,printf 関数は,format 関数を使ってフォーマットした文字 列を標準出力に印字する関数である.
解答例
structure Format : FORMAT =
struct
exception formatError
structure S = Substring
datatype kind = INT of StringCvt.radix
| REAL of StringCvt.realfmt
| STRING
| BOOL
datatype align = LEFT | RIGHT
datatype format =
LITERAL of string
| SPEC of {kind:kind,width:int option,align:align}
datatype argument = I of int
| R of real
| S of string
| B of bool
fun formatData {kind,width,align} data=
let val body =
case (kind,data) of
(INT radix,I i) => Int.fmt radix i
| (REAL fmt,R r) => Real.fmt fmt r
| (STRING,S s) => s
| (BOOL,B b) => Bool.toString b
| _ => raise formatError
in case width of
NONE => body
| SOME w => (case align of
LEFT => StringCvt.padRight #" " w body
| RIGHT => StringCvt.padLeft #" " w body)
end
fun scanInt s =
let val r= Int.scan StringCvt.DEC S.getc s
in case r of NONE => (NONE,s)
| SOME(n,s) => (SOME n,s)
end
fun oneFormat s =
let val s = S.triml 1 s
in if S.isPrefix "%" s then (LITERAL "%",S.triml 1 s)
else
let val (a,s) = if S.isPrefix "-" s
then (LEFT,S.triml 1 s)
else (RIGHT,s)
val (w,s) = scanInt s
val (c,s) = case S.getc s of NONE => raise formatError
| SOME s => s
in (SPEC {width=w,align=a,
kind=case c of
#"d" => INT StringCvt.DEC
| #"s" => STRING
| #"f" => REAL (StringCvt.FIX NONE)
| #"e" => REAL (StringCvt.SCI NONE)
| #"g" => REAL (StringCvt.GEN NONE)
| _ => raise formatError},
s)
end
end
fun parse s =
let
val (s1,s) = StringCvt.splitl (fn c => c <> #"%") S.getc s
val prefix = if s1 = "" then nil
else [LITERAL s1]
in if S.isEmpty s then prefix
else let val (f,s) = oneFormat s
val L = parse s
in prefix@(f::L)
end
end
fun format s L =
let val FL = parse (S.full s)
fun splice (h::t) L =
(case h of
LITERAL s => s ^ (splice t L)
| SPEC s => (formatData s (List.hd L) ^ (splice t (List.tl L))))
| splice nil l = ""
in
(splice FL L)
end
fun printf s L = print (format s L)
end
問 16.10
日時を表す書式を以下のように定める.
| %H | 24時間制の時間(00から23) |
| %I | 12時間制の時間(01から12) |
| %k | 24時間制の時間(0から23) |
| %M | 分 |
| %S | 秒 |
| %d | 日(01から31) |
| %m | 月(01から12) |
| %Y | 年 |
format の定義を参考にして,日時に関する以下の埋め込み書式指定を含む 文字列を受け取り,日時をプリントする関数
showTime : string -> unit
を書け. たとえば
- showTime "The time is %H hour %M minutes on %m/%d/%Y.\n"; The time is 21 hour 04 minutes on 9/9/2000. val it = () : unit
のように動作をする.
解答例
structure ShowTime =
struct
local
structure S = Substring
in
exception FormatError
fun dH t = StringCvt.padLeft #"0" 2 (Int.toString (Date.hour (Date.fromTimeLocal t)))
fun dI t =
let val h = Date.hour (Date.fromTimeLocal t)
val i = if h = 0 then 12 else if h > 12 then h - 12 else h
in StringCvt.padLeft #"0" 2 (Int.toString i)
end
fun dk t = Int.toString (Date.hour (Date.fromTimeLocal t))
fun dM t = Int.toString (Date.minute (Date.fromTimeLocal t))
fun dS t = Int.toString (Date.second (Date.fromTimeLocal t))
fun dd t= StringCvt.padLeft #"0" 2
(Int.toString (Date.day (Date.fromTimeLocal t)))
fun dm t = case Date.month (Date.fromTimeLocal t) of
Date.Jan => "01"
| Date.Feb => "02"
| Date.Mar => "03"
| Date.Apr => "04"
| Date.May => "05"
| Date.Jun => "06"
| Date.Jul => "07"
| Date.Aug => "08"
| Date.Sep => "09"
| Date.Oct => "10"
| Date.Nov => "11"
| Date.Dec => "12"
fun dY t = Int.toString (Date.year (Date.fromTimeLocal t))
datatype spec = EMBED of Time.time -> string | LITERAL of string
fun oneFormat s =
let val s = S.triml 1 s
in if S.isPrefix "%" s then (LITERAL "%",S.triml 1 s)
else
let
val (c,s) = case S.getc s of NONE => raise FormatError
| SOME s => s
in (EMBED (case c of
#"H" => dH
| #"I" => dI
| #"k" => dk
| #"M" => dM
| #"S" => dS
| #"d" => dd
| #"m" => dm
| #"Y" => dY
| _ => raise FormatError),
s)
end
end
fun parse s =
let
val (s1,s) = StringCvt.splitl (fn c => c <> #"%") S.getc s
val prefix = if s1 = "" then nil
else [LITERAL s1]
in if S.isEmpty s then prefix
else let val (f,s) = oneFormat s
val L = parse s
in prefix@(f::L)
end
end
fun format s tm =
let val FL = parse (S.full s)
fun splice (h::t) =
(case h of
LITERAL s => s ^ (splice t)
| EMBED f => f tm ^ (splice t))
| splice nil = ""
in
splice FL
end
fun showTime s =
print (format s (Time.now()))
end
end
問 16.11
Format ストラクチャを用いて,第13章で作成したソート関 数の評価プログラムの印字処理部分を書き直せ.
解答例 以下に変更部分の例を示す。
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’))
fun printLine (n,a,c) =
Format.printf "%20d%20d%20f\n" [Format.I n, Format.I a, Format.R c]
in
(Format.printf
"%20s%20s%20s\n"
[Format.S "array size", Format.S "milli-sec.", Format.S "micro s./nlogn"];
map printLine L’;
print "------------------------------------------------------------\n";
Format.printf "%40s%20f\n" [Format.S "avarage", Format.R av]
)
end