16章 データフォーマッティング
16.2
空白区切りの文字列を読み込んで整数を返す関数を作る.
以下動作の様子
- val f = readInt "123 345 abc"; - f (); (* 呼び出す毎に *) val it = SOME 123 : int option - f (); (* 整数を切り出す *) val it = SOME 345 : int option - f (); val it = NONE : int option
実装(回答)
val insScan = Int.scan StringCvt.DEC Substring.getc; fun readInt (s:string) : unit -> int option = let val rs = ref $ Substring.full s fun readImpl () = (case intScan (!rs) of SOME (f,sub) => (rs := sub; SOME f) | NONE => (rs := Substring.full ""; NONE)) in readImpl end
16.3 readReal, readBoolを作る
めんどいのでReal版だけ :p
fun readReal s = let val rs = ref $ Substring.full s fun readImpl () = (case Real.scan Substring.getc (!rs) of SOME (f,sub) => (rs := sub; SOME f) | NONE => (rs := Substring.full ""; NONE)) in readImpl end
16.4 genericReadInt
charを読み出すreaderからintを読み出す関数.
fun genericReadInt (reader : (char,'a) StringCvt.reader) (x : 'a) : unit -> int option = let val rx = ref x fun greader () = (case Int.scan StringCvt.DEC reader $ (!rx) of SOME (f,rest) => (rx := rest; SOME f) | NONE => NONE) in greader end
16.5
入力ストリームを受け取ってintを読み出す.
fun readIntFromStream (st : TextIO.instream) : unit -> int option = let (* ストリームreaderをでっち上げる *) fun reader s = case TextIO.input1 s of SOME c => SOME (c,s) | NONE => NONE val streader : unit -> int option = genericReadInt reader st in streader end
16.5.2
ファイルからテキストを読み込んで整数リストを返す
fun readIntFromFile (path : string) : int list = let val sread : unit -> int option = readIntFromStream $ TextIO.openIn path fun readtoken () = case sread () of SOME x => x::(readtoken()) | NONE => [] in readtoken () end
まとめ
- この本の中で使われている Substring.all はSML/NJには見あたらない.
代わりに Substring.full を使ってある.
- 読み込む関数に状態持たせるのはあんまりいい手じゃないような.
- 型アノテーションが書きにくい&読みにくい. 関数と型構築子はデータの前に付けるのに, 高階型の名前は後ろに付いていくのは直感に反するです. (int option listとか)
SML書いてたら楽しくなってきたので律儀に問題解いてしまう….