tags:

views:

1189

answers:

2

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.

+1  A: 

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
Juliet
Nice, thanks, I forget about "yield!"
Benjol
Also, if you want a list version of the code, is just easier to use seq { ... } |> Seq.to_list rather than rolling your own list version.
Juliet
But will it 'eat' lists of lists too? I still haven't grokked when Seq, List and #Seq cooperate or not.
Benjol
If by "will it 'eat' lists", you mean "is the function generalized to work on seq's and list's simultaneously", then yes, you can pass an object of type "list list 'a" and get back a "list 'a". Keep in mind that seq is a typedef of IEnumerable<_>, and list's implement the IEnumerable<_> interface.
Juliet
`seq { ... } |> Seq.to_list` is just `[...]`.
Jon Harrop
+1  A: 
let merge = function
  | [] -> []
  | xs::xss -> xs::[for _::xs in xss do yield! xs]
Jon Harrop