如何在F#中编写类似ZipN的函数?

Law*_*ton 5 f#

我想创建一个带有签名的函数,该函数seq<#seq<'a>> ->seq<seq<'a>>就像Zip方法一样,采用任意数量的输入序列(而不是Zip2和Zip3中的2或3)并返回序列序列而不是元组作为结果.

也就是说,给出以下输入:

[[1;2;3];
 [4;5;6];
 [7;8;9]]
Run Code Online (Sandbox Code Playgroud)

它将返回结果:[[1; 4; 7]; [2; 5; 8]; [3; 6; 9]]

除了序列而不是列表.

我对F#很新,但是我已经创建了一个能够满足我想要的功能,但我知道它可以改进.这不是尾递归,似乎它可能更简单,但我不知道如何.我还没有找到一种很好的方式来获得我想要的签名(接受,例如,int list list作为输入)而没有第二个功能.

我知道这可以直接使用枚举器实现,但我有兴趣以功能方式实现它.

这是我的代码:

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
Run Code Online (Sandbox Code Playgroud)

Dan*_*iel 8

let zipn items = items |> Matrix.Generic.ofSeq |> Matrix.Generic.transpose
Run Code Online (Sandbox Code Playgroud)

或者,如果你真的想自己写:

let zipn items = 
  let rec loop items =
    seq {
      match items with
      | [] -> ()
      | _ -> 
        match zipOne ([], []) items with
        | Some(xs, rest) -> 
          yield xs
          yield! loop rest
        | None -> ()
    }
  and zipOne (acc, rest) = function
    | [] -> Some(List.rev acc, List.rev rest)
    | []::_ -> None
    | (x::xs)::ys -> zipOne (x::acc, xs::rest) ys
  loop items
Run Code Online (Sandbox Code Playgroud)