2012-11-22 68 views
3

我想從我的示例簡化吸取以下教訓的行爲:枚舉和處理在F#

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 
  • 是枚舉「恩」設置的獨立的它被抓獲? (我想這是,但有什麼要說的/材料閱讀此行爲旁邊this msdn doc on ressources

  • 爲什麼它的工作,如果序列首先轉換爲列表?

編輯:這只是一個玩具的例子來說明一個行爲,而不是要遵循。 直接操作枚舉器的原因很少。

+0

「繁榮」是什麼意思?你看到了什麼症狀?讓人們猜測並不會引起很好的輸入。 – ildjarn

+0

補充說明信息 – nicolas

回答

7

using函數在lambda函數返回後儘快配置枚舉器。但是,lambda函數使用Seq.unfold創建一個惰性序列,而在之後,惰性序列訪問枚舉器,該序列從groupedEnum返回。

你既可以(通過添加List.ofSeq有)充分評估內部using整個序列,或者你需要調用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如果發生異常(然後返回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#列表類型返回的枚舉器完全忽略Dispose並繼續工作,而如果您在由string返回的枚舉器上調用Dispose,則枚舉器不能再次使用。 (這可以說是F#列表類型有點奇怪的行爲。)

+0

這個「seq {}」表達式有多好看和簡單。在大多數情況下,去統計員級別是不必要的,並且有代碼味道,只是玩弄它而已。實際上列表枚舉器沒有做任何事情https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/prim-types.fs – nicolas

+0

我不確定它是否因爲某種原因被省略,但爲了讓第一個代碼塊工作,在Seq.unfold之後我需要另一個'()' – Maslow