How do I write a ZipN-like function in F#?

Viewed 718

I want to create a function with the signature seq<#seq<'a>> ->seq<seq<'a>> that acts like a Zip method taking a sequence of an arbitrary number of input sequences (instead of 2 or 3 as in Zip2 and Zip3) and returning a sequence of sequences instead of tuples as a result.

That is, given the following input:

[[1;2;3];
 [4;5;6];
 [7;8;9]]

it will return the result: [[1;4;7]; [2;5;8]; [3;6;9]]

except with sequences instead of lists.

I am very new to F#, but I have created a function that does what I want, but I know it can be improved. It's not tail recursive and it seems like it could be simpler, but I don't know how yet. I also haven't found a good way to get the signature the way I want (accepting, e.g., an int list list as input) without a second function.

I know this could be implemented using enumerators directly, but I'm interested in doing it in a functional manner.

Here's my code:

let private Tail seq = Seq.skip 1 seq
let private HasLengthNoMoreThan n = Seq.skip n >> Seq.isEmpty

let rec ZipN_core = function
    | seqs when seqs |> Seq.isEmpty -> Seq.empty
    | seqs when seqs |> Seq.exists Seq.isEmpty -> Seq.empty
    | seqs ->
        let head = seqs |> Seq.map Seq.head
        let tail = seqs |> Seq.map Tail |> ZipN_core
        Seq.append (Seq.singleton head) tail

// Required to change the signature of the parameter from seq<seq<'a> to seq<#seq<'a>>
let ZipN seqs = seqs |> Seq.map (fun x -> x |> Seq.map (fun y -> y)) |> ZipN_core
5 Answers

Another option:

let zipN ls =
    let rec loop (a,b) =
        match b with
        |l when List.head l = [] -> a
        |l ->
            let x1,x2 =
                (([],[]),l)
                ||> List.fold (fun acc elem ->
                    match acc,elem with
                    |(ah,at),eh::et -> ah@[eh],at@[et]
                    |_ -> acc)
            loop (a@[x1],x2)
    loop ([],ls)
Related