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つよい.