1
votes

I'm trying to recreate my own function of Seq.zip. It needs to do the exact same thing as Seq.zip but only using seq to create it. Here is my code so far:

let rec seqZip s1 s2 =
    seq {
        let e1 = Seq.item 0 s1
        let e2 = Seq.item 0 s2

        let rec helper s1 s2 n =
            match s1, s2 with
            | s1, s2 when n > s1.Length && n > s2.Length -> yield ()
            | s1, s2 -> yield (Seq.item (n+1) s1, Seq.item (n+1) s2)

        helper s1 s2 0
    } 

I'm not sure I even need a helper function in there but do you have any suggestions?

2

2 Answers

6
votes

The type declaration seq<'a> is a type alias for System.Collections.Generic.IEnumerable<'a>. That interface provides an enumerator with which we can iterate through the collection, all higher level functions will also depend on these interfaces. Therefore, iterating the sequence by means of the MoveNext() method and the Current property should be the most efficient way.

let zip (s1 : seq<_>) (s2 : seq<_>) =
    use e1 = s1.GetEnumerator()
    use e2 = s2.GetEnumerator()
    let rec loop () = seq{
        if e1.MoveNext() && e2.MoveNext() then
            yield e1.Current, e2.Current
            yield! loop() }
    loop()

zip [1..3] [11..14] |> Seq.toList
// val it : (int * int) list = [(1, 11); (2, 12); (3, 13)]
Seq.zip [1..3] [11..14] |> Seq.toList
// val it : (int * int) list = [(1, 11); (2, 12); (3, 13)]

The nested helper function nicely encapsulates the recursion.

0
votes

A more sequence-oriented solution is:

let rec seqzip (a: 'a seq) (b: 'b seq): ('a*'b) seq =
    match a with
    | empty when Seq.isEmpty empty -> Seq.empty<'a*'b>
    | aa ->
        match b with
        | empty when Seq.isEmpty empty -> Seq.empty<'a*'b>
        | bb -> seq{
            yield (Seq.head aa, Seq.head bb)
            yield! (seqzip (Seq.tail aa) (Seq.tail bb))
            }