繁体   English   中英

是否可以使用序列进行尾调用优化f#中的分组功能?

[英]Is it possible to tail call optimize a grouping function in f# using sequences?

这是我的未优化尾调用的尝试,因为我需要配置枚举器:

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我会很茫然。 我真的很希望能够使用通用序列来完成这项工作。

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

您可以将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() }

甚至更好的是,您可以使用use构造实现从Group返回的序列:

    seq {
        use i = items.GetEnumerator ()
        // This is still not-tail recursive
        yield! some i seed } 

实际上,我认为这比原始代码更正确,因为它仅调用一次Dispose方法。 在您的版本中,每次执行进入some执行时,它将被调用一次(这取决于异常发生之前处理的元素数量)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM