ppaul74
ppaul74

Reputation: 751

OCaml code that works on 2 lists. Is there a better way of doing this

I have to iterate over 2 lists. One starts off as a list of empty sublists and the second one has the max length for each of the sublists that are in the first one.

Example; list1 = [[];[];[];]; list2 = [1;2;3]

I need to fill out the empty sublists in list1 ensuring that the length of the sublists never exceed the corresponding integer in list2. To that end, I wrote the following function, that given an element, elem and 2 two lists list and list, will fill out the sublists.

let mapfn elem list1 list2= 
    let d = ref 1 in 
        List.map2 (fun a b  -> if ((List.length a) < b) && (!d=1)
                             then  (incr d ; List.append a [elem])
                              else  a )
                        list1 list2

;;

I can now call this function repeatedly on the elements of a list and get the final answer I need

This function works as expected. But I am little bothered by the need to use the int ref d. Is there a better way for me to do this.

Upvotes: 3

Views: 585

Answers (2)

user593999
user593999

Reputation:

I always find it worthwhile to split the problem into byte-sized pieces that can be composed together to form a solution. You want to pad or truncate lists to a given length; this is easy to do in two steps, first pad, then truncate:

let all x = let rec xs = x :: xs in xs

let rec take n = function
| []           -> []
| _ when n = 0 -> []
| x :: xs      -> x :: take (pred n) xs

all creates an infinite list by repeating a value, while take extracts the prefix sublist of at most the given length. With these two, padding and truncating is very straightforwad:

let pad_trim e n l = take n (l @ all e)

(it might be a bit surprising that this actually works in a strict language like OCaml). With that defined, your required function is simply:

let mapfn elem list1 list2 = List.map2 (pad_trim elem) list2 list1

that is, taking the second list as a list of specified lengths, pad each of the lists in the first list to that length with the supplied padding element. For instance, mapfn 42 [[];[];[]] [1;2;3] gives [[42]; [42; 42]; [42; 42; 42]]. If this is not what you need, you can tweak the parts and their assembly to suit your requirements.

Upvotes: 4

gasche
gasche

Reputation: 31459

Are you looking for something like that?

let fill_list elem lengths =
  let rec fill acc = function
    | 0 -> acc
    | n -> fill (elem :: acc) (n - 1) in
  let accumulators = List.map (fun _ -> []) lengths in
  List.map2 fill accumulators lengths

(* toplevel test *)   
# let test = fill_list 42 [1; 3];;
val test : int list list = [[42]; [42; 42; 42]]

(I couldn't make sense of the first list of empty lists in your question, but I suspect it may be the accumulators for the tail-rec fill function.)

Upvotes: 3

Related Questions