这是我的尝试,它不是尾部调用优化,因为我需要处理枚举器:
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
以下是示例用法:
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
如果我可以避免使用枚举器(即Seq.head AND Seq.tail
),我可以使它工作但没有Seq.tail
我不知所措。我真的希望用通用序列来完成这项工作..
我知道这段代码在当前状态下无效,因为我最终会多次处理枚举器。
答案 0 :(得分:5)
您可以将try .. finally
块从内部some
函数(在每次迭代中输入的位置)移动到main函数。然后内部递归函数some
变为尾递归:
let Group func seed (items : seq<'t>) =
// The handling of exceptions is done by the caller,
// so 'some' does not need to handle exceptions...
let rec some (i : IEnumerator<'t>) state = seq {
if i.MoveNext()
then
let newstate, iscomplete = func (i.Current) state
if iscomplete then
yield newstate
// Recursive call in tail-call position
yield! some i newstate
else
yield state }
// Return a sequence that wraps the obtains the IEnumerator
// and guarantees that it gets disposed if 'some' fails
seq {
let i = items.GetEnumerator ()
try
// This is still not-tail recursive
yield! some i seed
finally
i.Dispose() }
甚至更好,您可以使用Group
构造实现从use
返回的序列:
seq {
use i = items.GetEnumerator ()
// This is still not-tail recursive
yield! some i seed }
实际上,我认为这比原始代码更正确,因为它只调用Dispose
方法一次。在您的版本中,每次执行进入some
时都会调用一次(这取决于异常发生前处理的元素数量)。