4

簡略化した例から、次の動作についてのレッスンを描画しようとしています。

let groupedEnum  (input: 'a seq) =
   using (input.GetEnumerator()) (fun en ->
   Seq.unfold(fun _ -> 
                  if en.MoveNext() then 
                     Some(en.Current, ())
                  else None) ()
   )


//WORKS    
let c = groupedEnum    ("11111122334569999"   |>  List.ofSeq ) |>  List.ofSeq 

//BOOM !!  System.NullReferenceException
let c = groupedEnum    ("11111122334569999"                  ) |>  List.ofSeq

編集:これは、行動を説明するための単なるおもちゃの例であり、従うべきではありません。列挙子を直接操作する正当な理由はほとんどありません。

4

1 に答える 1

8

このusing関数は、ラムダ関数が戻るとすぐに列挙子を破棄します。ただし、ラムダ関数は を使用して遅延シーケンスを作成し、遅延シーケンスは からシーケンスが返された後Seq.unfoldに列挙子にアクセスします。groupedEnum

内部のシーケンス全体を完全に評価するかusing(そこに追加することにより) 、生成されたシーケンスの最後に達したときList.ofSeqに呼び出す必要があります。Dispose

let groupedEnum  (input: 'a seq) =
   let en = input.GetEnumerator()
   Seq.unfold(fun _ -> 
       if en.MoveNext() then 
           Some(en.Current, ())
       else 
           en.Dispose()
           None)

try .. withこの場合、例外処理は非常に難しくなりますが、ボディをラップして、例外が発生した場合に呼び出すDispose(そして返す)のが 1 つの方法だと思いNoneます。

代わりにシーケンス式を使用すると、の意味がuse変わり、シーケンスの最後に到達した後に列挙子が自動的に破棄されます (レイジー シーケンスが返されたときではありません)。したがって、面倒な作業は自動的に行われるため、シーケンス式を使用することをお勧めします。

let groupedEnum  (input: 'a seq) = seq {
   use en = input.GetEnumerator()
   let rec loop () = seq {
      if en.MoveNext() then 
         yield en.Current
         yield! loop () }
   yield! loop () }

編集そして、なぜあなたの最初の例でそれが機能するのですか? F# の list 型によって返される列挙子は単に無視され、引き続き機能しますが、 a によって返される列挙子Disposeを呼び出すと、その列挙子を再度使用することはできません。(これは間違いなく、F# リスト型の少し奇妙な動作です。)Disposestring

于 2012-11-22T12:35:05.673 に答える