for-loopを実装する
SMLにはwhile-loopがありますが,for-loopはありません. しかし悲しいことに,時にはあった方が便利です. 受け入れましょう.
要は副作用を起こす関数に整数を渡してやればよいのです.
(* i -> j のループ *) fun to (i,j) f = let fun loop i = if i<j then (f i; loop (i+1)) else () in loop i end; fun downto (i,j) f = let fun loop i = if i>j then (f (i-1);loop (i-1)) else (); in loop i end; infix to downto fun for x = x (* 見栄えの都合… *) fun printInt x = print(Int.toString x)
使ってみます.
for (1 to 10) (printInt) --> 123456789 for (10 downto 0) (printInt) --> 9876543210
あっさり出来ましたね.
イテレーション対象のバリエーション
forに渡すのは単なるintを適用する関数なので,こんなことも出来ます
(* List.app : ('a -> unit) -> 'a list -> unit *) fun inList xs = fn f => List.app f xs for (inList [1,3,5]) (printInt) --> 135
ストリームに対してイテレーションも可
(* using : ('b, 'a) StringCvt.reader -> 'a -> ('b -> 'unit) -> unit *) fun using get s f = let fun using' s = case get s (* ストリームから取り出す *) of SOME(x,rest)=>(f x; using' rest) | NONE => () in using' s end local open Substring in (* 文字列のストリームに対してiter *) for (using getc (full "abcdefg")) (fn c => print (str c)); end --> "abcdefg"
イテレータコンビネータ
任意の入力を操作したり,組み合わせたりも出来ます.
- 入力をフィルタする
infix 1 when fun (f when p) g = f (fn i=> if p i then g i else ()) - ((0 to 10) when (fn i=>i mod 2=0)) (printInt); 02468
- 複数の入力を組み合わせる
infix 0 ** fun (a ** b) f = a (fn i=> b (fn j=> f (i,j))) (* ('a t * 'b t) -> ('a * 'b) t *) - ((0 to 3) ** (2 to 4)) (fn(i,j)=>print(concat["(",Int.toString i,",",Int.toString j,")"])); (0,2)(0,3)(1,2)(1,3)(2,2)(2,3)
まとめ
- 参考>http://mlton.org/ForLoops
- ループはめんどくさいなーと思っていたのですが, 意外に柔軟な記述と組み合わせが可能ですね. MLつよい.