4
votes

Slowly getting the hang of List matching and tail recursion, I needed a function which 'stitches' a list of lists together leaving off intermediate values (easier to show than explain):

merge [[1;2;3];[3;4;5];[5;6;7]] //-> [1;2;3;4;5;6;7]

The code for the List.merge function looks like this:

///Like concat, but removes first value of each inner list except the first one
let merge lst = 
    let rec loop acc lst = 
        match lst with
        | [] -> acc
        | h::t -> 
            match acc with
            | [] -> loop (acc @ h) t
            | _ -> loop (acc @ (List.tl h)) t //first time omit first value
    loop [] lst

(OK, it's not quite like concat, because it only handles two levels of list)

Question: How to do this for a Seq of Seqs (without using a mutable flag)?

UPDATE (re comment from Juliet): My code creates 'paths' composed of 'segments' which are based on an option type:

type SegmentDef = Straight of float | Curve of float * float
let Project sampleinterval segdefs = //('clever' code here)

When I do a List.map (Project 1.) ListOfSegmentDefs, I get back a list where each segment begins on the same point where the previous segment ends. I want to join these lists together to get a Path, keeping only the 'top/tail' of each overlap - but I don't need to do a 'Set', because I know that I don't have any other duplicates.

2
Its not exactly clear what you're referring to by "intermediate values". Are you trying to flatten the list and remove duplicates at the same time? Trying to chop off the first item of each sub list? - Juliet

2 Answers

4
votes

This is essentially the same as your first solution, but a little more succinct:

let flatten l =
    seq {
        yield Seq.hd (Seq.hd l) (* first item of first list *)
        for a in l do yield! (Seq.skip 1 a) (* other items *)
    }

[Edit to add]:

If you need a List version of this code, use append |> Seq.to_list at the end of your method:

let flatten l =
    seq {
        yield Seq.hd (Seq.hd l) (* first item of first list *)
        for a in l do yield! (Seq.skip 1 a) (* other items *)
    } |> Seq.to_list
4
votes
let merge = function
  | [] -> []
  | xs::xss -> xs @ [for _::xs in xss do yield! xs]

or:

let merge = function
  | [] -> []
  | xs::xss -> xs @ List.collect List.tail xss