2

这是我的尝试,它没有优化尾调用,因为我需要处理枚举器:

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我会不知所措。我真的希望用通用序列来完成这项工作..

仅供参考,我知道这段代码在当前状态下无法工作,因为我最终会多次处理枚举器。

4

1 回答 1

5

您可以将try .. finally块从内部some函数(在每次迭代期间输入)移动到主函数。然后内部递归函数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(这取决于在发生异常之前处理的元素数量)。

于 2012-01-24T00:45:34.967 回答