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 } 

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 enters some (which would depend on the number of elements processed before an exception happens).

于 2012-01-24T00:45:34.967 に答える