[MLton] A Vector/Fold challenge
Stephen Weeks
MLton@mlton.org
Fri, 5 May 2006 20:42:34 -0700
> This has been itching me for a few weeks, and while I'm certain I
> would learn more by doing it myself, it will probably come pretty
> easy to the master Fold-ers.
My solution is at the end of this message. I didn't use fold; more
important is the inductive construction of the sub and update
functions for products of vectors. One could throw fold on top to
build the "numbers" more concisely, but I'm not sure it's necessary
here.
To make things effecient, I needed an improved version of
MLton.Vector.create with the following signature.
val create: int -> {done: unit -> 'a vector,
sub: int -> 'a,
update: int * 'a -> unit}
The problem with the old version is that it controlled the stack,
which made it impossible to create several vectors simultaneously, and
was needed to do this well. Here's the implementation (inside the
basis) for the new create.
------------------------------------------------------------
fun create n =
let
val a = Primitive.Array.array n
val subLim = ref 0
fun sub i =
if Primitive.safe andalso Primitive.Int.geu (i, !subLim) then
raise Subscript
else
Primitive.Array.sub (a, i)
val updateLim = ref 0
fun update (i, x) =
if Primitive.safe andalso Primitive.Int.geu (i, !updateLim) then
if i = !updateLim andalso Primitive.Int.< (i, n) then
(Primitive.Array.update (a, i, x);
subLim := i + 1;
updateLim := i + 1)
else
raise Subscript
else
Primitive.Array.update (a, i, x)
val gotIt = ref false
fun done () =
if !gotIt then
raise Fail "already got vector"
else
if n = !updateLim then
(gotIt := true;
updateLim := 0;
fromArray a)
else
raise Fail "vector not full"
in
{done = done,
sub = sub,
update = update}
end
------------------------------------------------------------
I propose to replace the current MLton.Vector.create with this one, as
it is strictly more powerful.
Here's the solution. Let me know if it doesn't make sense.
--------------------------------------------------------------------------------
datatype ('a, 'b) prod = & of 'a * 'b
infix &
(* Uncomment the following to test in SML/NJ *)
(* structure MLton =
* struct
* structure Vector =
* struct
* val create: int -> {done: unit -> 'a vector,
* sub: int -> 'a,
* update: int * 'a -> unit} =
* fn _ => raise Fail "MLton.Vector.create"
* end
* end
*)
structure Int =
struct
fun for (start, stop, f) =
let
fun loop i = if i = stop then () else (f i; loop (i + 1))
in
loop start
end
end
structure VectorMap =
struct
datatype ('a, 'b, 'c, 'd) t =
T of {create: int -> {done: unit -> 'a,
update: int * 'b -> unit},
size: 'c -> int,
sub: 'c * int -> 'd}
fun more (make: unit -> ('a, 'b, 'c, 'd) t) () =
let
val T {create, size, sub} = make ()
val create =
fn n =>
let
val {done = d1, update = u1} = create n
val {done = d2, update = u2, ...} = MLton.Vector.create n
in
{done = fn () => d1 () & d2 (),
update = fn (i, x1 & x2) => (u1 (i, x1); u2 (i, x2))}
end
val size = fn vs & v =>
let
val n = Vector.length v
val n' = size vs
in
if n = n' then
n
else
raise Fail "vectors of different sizes"
end
val sub = fn (vs & v, i) => sub (vs, i) & Vector.sub (v, i)
in
T {create = create,
size = size,
sub = sub}
end
fun one () =
let
fun create i =
let
val {done, update, ...} = MLton.Vector.create i
in
{done = done,
update = update}
end
in
T {create = create,
size = Vector.length,
sub = Vector.sub}
end
fun two () = more one ()
fun three () = more two ()
fun mapNtoM (vs, n, m, f) =
let
val T {size, sub, ...} = n ()
val T {create, ...} = m ()
val n = size vs
val {done, update} = create n
val () = Int.for (0, n, fn i => update (i, f (sub (vs, i))))
in
done ()
end
end
val v1 = Vector.tabulate (10, fn i => i)
val v2 = Vector.tabulate (10, fn i => 2 * i)
val va & vb & vc =
VectorMap.mapNtoM
(v1 & v2, VectorMap.two, VectorMap.three,
fn x & y => (x + y) & (x * y) & (chr x))