(*
 * This file is a collection of functions to do an assortment of
 * imperative-style loops.  See 501 lecture notes for further
 * discussion.  Also, the following individual files contain looping
 * examples and further discussion: for.ml, while.ml, loops-tests.ml.
 *)

structure Loops = struct

exception EmptyLoop and ExitLoop

(*
 * General purpose for-loop, returning the value of of the loop vars as of the
 * last iteration.  If no iterations occur, EmptyLoop is raised to the caller.
 *)
fun for(start:int, until, by, vars:'v, body:int*'v->'v): 'v =
    if start>until then
	raise EmptyLoop
    else
	let
	    val result = body(start, vars)
	in
	    for(start+by, until, by, result, body)
		handle EmptyLoop => result
	end

(*
 * A la for, but returns list of all loop iterations.  If no iterations, nil
 * is returned (hence, no exception need be raised).
 *)
fun forl(start:int, until, by, vars:'v, body:int*'v->'v): 'v list =
    if start>until then
	[]
    else
	let
	    val result = body(start, vars)
	in
	    forl(start+by, until, by, result, body) @ [result]
	end;

(*
 * C-Shell-style list iterator.
 *)
fun foreach(l:'x list, vars:'y, body:'x*'y->'y):'y =
    if null(l) then
	raise EmptyLoop	
    else
	let
	    val result = body(hd(l), vars)
	in
	    foreach(tl(l), result, body)
		handle EmptyLoop => result
	end

(*
 * A la foreach, but returns list of all iteration results.
 *)
fun foreachl(l:'x list, vars:'y, body:'x*'y->'y):'y list =
    if null(l) then
	[]
    else
	let
	    val result = body(hd(l), vars)
	in
	    foreachl(tl(l), result, body) @ [result]
	end

(*
 * General-purpose while loop, returning list of body iterations.  It's named
 * while1, since "while" is an SML keyword.
 *)
fun while1(test:'x->bool, vars:'x, body:'x->'x): ('x) list =
    if test(vars) then
	let
	    val result = body(vars)
	in
	    while1(test, result, body) @ [result]
	end
    else
	[]

fun loop(vars, body) =
    let
	val vars = body(vars)
    in
	loop(vars, body)
    end

end (* structure Loops *)