Bra*_*rad 2 f# tail-recursion tail-call-optimization
这是我的尝试,不是尾部调用优化,因为我需要处置枚举器:
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
Run Code Online (Sandbox Code Playgroud)
以下是示例用法:
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
Run Code Online (Sandbox Code Playgroud)
如果我可以避免使用调查员(即Seq.head AND Seq.tail),我可以使它工作,但没有Seq.tail我不知所措.我真的希望用通用序列来完成这项工作.
还有,我知道这段代码在当前状态下不起作用,因为我最终会多次处理枚举器.
您可以将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() }
Run Code Online (Sandbox Code Playgroud)
或者甚至更好,您可以实现Group使用use构造返回的序列:
seq {
use i = items.GetEnumerator ()
// This is still not-tail recursive
yield! some i seed }
Run Code Online (Sandbox Code Playgroud)
实际上,我认为这比原始代码更正确,因为它只调用Dispose一次方法.在您的版本中,每次执行时都会调用一次some(这取决于异常发生之前处理的元素数量).