patternMinor
Recursive implementation of a zip function in F#
Viewed 0 times
implementationrecursivefunctionzip
Problem
I'm learning F# from Tomas Petricek's "Real-World Functional Programming" book. As suggested at one point in the book, I started implementing a
What I don't like about my code:
zip function using a recursive sequence expression. I'm not very happy with my implementation and I would like to know how F# devs would refactor my implementation. I know that the original zip function is implemented using map2 but I want to stick with the recursive sequence expression. Any suggestion would expand my horizon.// Create the zip function
let zipRec first second =
let rec loop r xs ys = seq {
if Seq.isEmpty xs || Seq.isEmpty ys then yield r
else
let r = Seq.append r (Seq.singleton((Seq.item 0 xs, Seq.item 0 ys)))
yield! loop r (Seq.tail xs) (Seq.tail ys) }
loop Seq.empty first second
// test
zipRec [1;2;3;4] [|"one"; "two" |] |> List.ofSeq |> printfn "%A"What I don't like about my code:
- It feels too imperative
- I'm using a lot of the functions in the Seq module. Can this be written more F# idiomatic (short, concise, using more functional programming style)?
- Does the code perform fast enough for real world scenarios? (I've used tail recursion)
- Is the code robust enough (in regards to exceptions)?
Solution
Based upon my comment on that answer I've toyed a little to make a recursive sequence expression with roughly the same performance (on my computer at least) as
without the recursive requirement it would be simpler this way
Seq.ziplet zipRec (s1: _ seq) (s2: _ seq) =
use e1 = s1.GetEnumerator ()
use e2 = s2.GetEnumerator ()
let rec loop hasMoved1 hasMoved2 = seq {
if hasMoved1 && hasMoved2 then
yield e1.Current, e2.Current
yield! loop (e1.MoveNext ()) (e2.MoveNext ())
}
loop (e1.MoveNext ()) (e2.MoveNext ())without the recursive requirement it would be simpler this way
let zipRec (s1: _ seq) (s2: _ seq) = seq {
use e1 = s1.GetEnumerator ()
use e2 = s2.GetEnumerator ()
while e1.MoveNext () && e2.MoveNext () do
yield e1.Current, e2.Current
}Code Snippets
let zipRec (s1: _ seq) (s2: _ seq) =
use e1 = s1.GetEnumerator ()
use e2 = s2.GetEnumerator ()
let rec loop hasMoved1 hasMoved2 = seq {
if hasMoved1 && hasMoved2 then
yield e1.Current, e2.Current
yield! loop (e1.MoveNext ()) (e2.MoveNext ())
}
loop (e1.MoveNext ()) (e2.MoveNext ())let zipRec (s1: _ seq) (s2: _ seq) = seq {
use e1 = s1.GetEnumerator ()
use e2 = s2.GetEnumerator ()
while e1.MoveNext () && e2.MoveNext () do
yield e1.Current, e2.Current
}Context
StackExchange Code Review Q#134173, answer score: 6
Revisions (0)
No revisions yet.