I’ve been busy working on a project in SML/NJ, and in the process I ended up writing a few generic list functions. I’ve extracted these into a separate module, and I’m putting it up here. Warning: These are just quick hacks (but they work (I think!)), and not all of them will be useful outside of my project.
Briefly, here’s what this contains:
A specialized version of foldl, a join function similair to Ruby’s, list generating functions and range functions, map and app functions that also pass in the index of the element, and a special kind of cross product function.
Anyway here goes (sorry for the meaningless syntax highlighting, SML/NJ is of course not supported by the WordPress sourcecode plugin):
structure ListExtension = struct
exception InvalidArgument of string;
(*
* folds left across a list, applying a binary function pairwise to its members.
* Eg, foldLeft f [a,b,c] = f(f(a,b),c);
*)
fun foldLeft f [] = raise InvalidArgument "Empty list to foldLeft"
|foldLeft f (h::[]) = h
|foldLeft f (h::t) = foldl (fn (a,b) => f (b,a)) h t
(*
* 'joins' a list into a string, converting each member into a string by the
* given function, and interspersing the given string
*)
local
fun join_aux s f (h::t) res =
if res = "" then
join_aux s f t (f h)
else
join_aux s f t (res ^ s ^ (f h))
|join_aux s f [] res = res
in
fun join s f h = join_aux s f h ""
end
(*
* Convenience function when the conversion is identity
* eg joinString "+" ["a", "b", "c"] = "a+b+c"
*)
fun joinString s h = join s (fn a => a) h
(*
* List generators.
*)
local
fun step_aux n inc lim f l =
if (inc > 0 andalso n > lim) orelse
(inc < 0 andalso n < lim) orelse
(inc = 0) then
l
else
step_aux (n + inc) inc lim f ((f n)::l)
in
fun step start fin inc f =
if start <= fin then
List.rev (step_aux start inc fin f [])
else
List.rev (step_aux start (~inc) fin f [])
end
fun downfrom n f = step n 0 1 f
fun upto n f = step 0 n 1 f
fun fromto start fin f = step start fin 1 f
fun range from to = ste from to 1 (fn i => i)
fun rangeStep from to inc = step from to inc (fn i => i)
(*
* Map with index. Same as List.map, but also passes in the index as an
* argument to the given function
*)
local
fun mapi_aux f [] i res = (List.rev res)
|mapi_aux f (h::t) i res = mapi_aux f t (i+1) (f (i,h)::res)
in
fun mapi f l = mapi_aux f l 0 []
end
(*
* app with index. Same as List.app, but also passes in the index as an
* argument to the given function
*)
local
fun appi_aux f [] i = ()
|appi_aux f (h::t) i = (f (i, h); appi_aux f t (i+1))
in
fun appi f l = appi_aux f l 0
end
(*
* Applies the function f to each element of the cross product of the two
* lists.
* f(a,b) is discarded if it is NONE, if it is SOME(c) then c is added to the result
*)
local
fun crossProduct_aux f (h::t) l result =
crossProduct_aux f t l ((List.mapPartial (f h) l) @ result)
|crossProduct_aux f [] l result = result
in
fun crossProduct f (a, b) = crossProduct_aux f (List.rev a) b []
end
end
Suggestions/ additions are welcome of course.


