Here is my attempt which is NOT tail call optimized because I need to dispose the enumerator:
let Group func seed (items : seq<'t>) =
let rec some (i : IEnumerator<'t>) state = seq {
try
if i.MoveNext()
then
let newstate, iscomplete = func (i.Current) state
if iscomplete
then
yield newstate
yield! some i newstate
else
yield state
finally
i.Dispose() }
some (items.GetEnumerator ()) seed
And here is example usage:
let buffer maxBufferSize items =
Group (fun item state ->
let newstate = [ item ] |> List.append state
if newstate.Length >= maxBufferSize
then (newstate, true)
else (newstate, false)) List.empty items
If I can avoid using the enumerator (i.e. Seq.head AND Seq.tail
) I could make it work but without Seq.tail
I am at a loss. I am really hoping to make this work with generic sequences..
And FYI I know this code won't work in its current state because I would end up disposing the enumerator multiple times.
You can move the
try .. finally
block from the innersome
function (where it is entered during every iteration) to the main function. Then the inner recursive functionsome
becomes tail-recursive:Or even better, you can implement the sequence that is returned from
Group
using theuse
construct:Actually, I think this is more correct than your original code, because it calls the
Dispose
method only once. In your version, it would be called once for every time the execution enterssome
(which would depend on the number of elements processed before an exception happens).