In this chapter, I intend to present a few more list-processing functions. More importantly, I would like to argue for the practice of actively identifying the need for generic list-processing functions during problem-solving.
There are many useful list-processing functions for handling two lists simultaneously. For instance, the following function list0_zip takes a pair of lists and returns a list of pairs:
// extern fun { a,b:t@ype } list0_zip (xs: list0(a), ys: list0(b)): list0($tup(a, b)) // implement {a,b} list0_zip(xs, ys) = ( case xs of | list0_nil() => list0_nil() | list0_cons(x, xs) => ( case+ ys of | list0_nil() => list0_nil() | list0_cons(y, ys) => list0_cons($tup(x, y), list0_zip<a,b>(xs, ys)) ) ) //
// extern fun {a ,b:t@ype} {c:t@ype} list0_map2 (xs: list0(a), ys: list0(b), fopr: cfun(a, b, c)): list0(c) // implement {a,b}{c} list0_map2 (xs, ys, fopr) = ( case xs of | list0_nil() => list0_nil() | list0_cons(x, xs) => ( case+ ys of | list0_nil() => list0_nil() | list0_cons(y, ys) => list0_cons(fopr(x, y), list0_map2<a,b><c>(xs, ys, fopr)) ) (* end of [list0_cons] *) ) //
There are various list-processing functions operating on ordered lists. For instance, the following function list0_merge is for merging two ordered lists into one:
// extern fun {a:t@ype} list0_merge ( xs: list0(a) , ys: list0(a), cmp: cfun(a, a, int)): list0(a) // implement {a}(*tmp*) list0_merge (xs0, ys0, cmp) = let // fun auxlst ( xs0: list0(a) , ys0: list0(a) ) : list0(a) = ( // case+ xs0 of | list0_nil() => ys0 | list0_cons (x1, xs1) => ( case+ ys0 of | list0_nil() => xs0 | list0_cons (y1, ys1) => let val sgn = cmp(x1, y1) in if (sgn <= 0) then list0_cons(x1, auxlst(xs1, ys0)) else list0_cons(y1, auxlst(xs0, ys1)) // end of [if] end // end of [list0_cons] ) (* end of [list0_cons] *) // ) (* end of [auxlst] *) // in auxlst(xs0, ys0) end // end of [list0_merge] //
// extern fun {a:t@ype} list0_mergesort (xs: list0(a), cmp: cfun(a, a, int)): list0(a) // implement {a}(*tmp*) list0_mergesort (xs, cmp) = let // // [msort]: // It is assumed // that length(xs) = n // fun msort (xs: list0(a), n: int): list0(a) = if (n >= 2) then let val n1 = n / 2 val xs1 = list0_take_exn(xs, n1) val xs2 = list0_drop_exn(xs, n1) in list0_merge<a>(msort(xs1, n1), msort(xs2, n-n1), cmp) end // end of [then] else (xs) // end of [else] // in msort(xs, list0_length<a>(xs)) end // end of [list0_mergesort] //
During problem-solving, it often pays if one actively looks for opptunities to generalize a specific function into one that can be given a meaningful description in a broader context. As an example, if one encounters a need to process all of the pairs formed with elements chosen from a given list, then one may want to implement the following function list0_choose2:
// extern fun {a:t@ype} list0_choose2 (xs: list0(a)): list0($tup(a, a)) // implement {a}(*tmp*) list0_choose2 (xs) = let // typedef aa = $tup(a, a) // in // case+ xs of | list0_nil() => list0_nil() | list0_cons(x0, xs) => list0_append<aa> (list0_map<a><aa>(xs, lam(x) => $tup(x0, x)), list0_choose2(xs)) // end // end of [list0_choose2] //
// extern fun {a:t@ype} list0_nchoose (xs: list0(a), n: int): list0(list0(a)) // implement {a}(*tmp*) list0_nchoose (xs, n) = auxlst(xs, n) where { // typedef xs = list0(a) // fun auxlst ( xs: xs, n: int ) : list0(xs) = ( if (n <= 0) then list0_sing(list0_nil()) else ( case+ xs of | list0_nil() => list0_nil() | list0_cons(x0, xs) => list0_append<xs>(list0_mapcons(x0, auxlst(xs, n-1)), auxlst(xs, n)) ) (* end of [else] *) ) // } (* end of [list0_nchoose] *) //
// extern fun {a:t@ype} list0_nchoose_rest (xs: list0(a), n: int): list0($tup(list0(a), list0(a))) // implement {a}(*tmp*) list0_nchoose_rest (xs, n) = auxlst(xs, n) where { // typedef xs = list0(a) typedef xsxs = $tup(xs, xs) // fun auxlst ( xs: xs, n: int ) : list0(xsxs) = ( if (n <= 0) then list0_cons ($tup(list0_nil(), xs), list0_nil()) else ( case+ xs of | list0_nil() => list0_nil() | list0_cons(x0, xs) => let val res1 = list0_map<xsxs><xsxs> ( auxlst(xs, n-1) , lam($tup(xs1, xs2)) => $tup(list0_cons(x0, xs1), xs2) ) val res2 = list0_map<xsxs><xsxs> ( auxlst(xs, n-0) , lam($tup(xs1, xs2)) => $tup(xs1, list0_cons(x0, xs2)) ) in list0_append<xsxs>(res1, res2) end // end of [list0_cons] ) (* end of [else] *) ) // } (* end of [list0_nchoose_rest] *)
The problem of enumerating all of the permutations of a given list is often used to test one's ability in constructing recursively defined functions. Let us see a solution to this problem given as follows:
// fun {a:t@ype} list0_permute ( xs: list0(a) ) : list0(list0(a)) = ( case+ xs of | list0_nil() => list0_cons(nil0(), nil0()) | list0_cons _ => let typedef xs = list0(a) typedef out = list0(xs) typedef inp = $tup(xs, xs) in list0_concat<xs> ( list0_map<inp><out> ( list0_nchoose_rest<a>(xs, 1) , lam($tup(ys, zs)) => list0_mapcons<a>(ys[0], list0_permute<a>(zs)) ) ) (* list0_concat *) end (* end of [list0_cons] *) ) //
Please find on-line the entirety of the code used in this chapter. The mentioned URL link(s) can be found as follows: