2010-05-19 60 views
2

什麼是實現在F#這個嵌套類的功能,優雅的方式?重寫簡單的C#的嵌套類

private class Aliaser { 
    private int _count; 
    internal Aliaser() { } 
    internal string GetNextAlias() { 
     return "t" + (_count++).ToString(); 
    } 
    } 

這是我第一次嘗試,但感覺應該有一個性感的一個班輪此:

let aliases = (Seq.initInfinite (sprintf "t%d")).GetEnumerator() 

let getNextAlias() = 
    aliases.MoveNext() |> ignore 
    aliases.Current 

回答

7

寫作的常用方法是創建一個封閉捕獲局部狀態的函數的例子:

let getNextAlias = 
    let count = ref 0 
    (fun() -> 
    count := !count + 1; 
    sprintf "t%d" (!count)) 

類型的getNextAlias簡直是unit -> string和當你重複地調用它時,它會返回字符串「t1」,「t2」,...這依賴於可變狀態,但是可變狀態對用戶是隱藏的。

至於是否可以做到這一點沒有可變狀態 - 簡單的答案是否定的,因爲當你調用具有相同參數的純功能性功能的兩倍,它必須返回相同的結果。因此,你必須寫東西用以下結構:

let alias, state1 = getNextAlias state0 
printf "first alias %s" alias 
let alias, state2 = getNextAlias state1 
printf "second alias %s" alias 
// ... 

正如你所看到的,你需要保留一些狀態,並在整個代碼維護。在F#中,處理這個問題的標準方法是使用可變狀態。在Haskell中,你可以使用國家單子,它允許您隱藏狀態的傳遞。使用實施from this question,你可以寫這樣的:

let getNextAlias = state { 
    let! n = getState 
    do! setState (n + 1) 
    return sprintf "t%d" n } 

let program = 
    state { 
    let! alias1 = getNextAlias() 
    let! alias2 = getNextAlias() 
    // ... 
    } 

execute progam 0 // execute with initial state 

這是很相似的其他計算如lazyseq,實際上 - 在state { .. }塊計算有一定的狀態,你可以通過提供初始值執行它們的狀態。然而,除非你有充分的理由要求純粹的功能解決方案,否則我更喜歡實際的F#編程的第一個版本。

+1

這很有幫助。你看到沒有聲明可變狀態的任何方式嗎? – Daniel 2010-05-19 21:03:46

+0

我可能會補充一件事:你可以使用'incr count'而不是'count:=!count + 1' – Daniel 2010-05-19 21:07:25

+0

我意識到必須有可變狀態_somewhere_,但我喜歡它以隱藏在預定義的模式(例如,懶惰,seq)。 – Daniel 2010-05-19 21:15:57

2

這裏是快速和骯髒的翻譯

type Aliaser() = 
    let mutable _count = 0 
    member x.GetNextAlias() = 
    let value = _count.ToString() 
    _count <- _count + 1 
    "t" + value 

一沒有狀態的更多功能方法是使用延續。

let createAliaser callWithValue = 
    let rec inner count = 
     let value = "t" + (count.ToString()) 
     callWithValue value (fun() -> inner (count + 1)) 
    inner 1 

這是一個聲明,這將調用該函數callWithValue兩者的價值和功能,執行與下一個值重複。

而這裏的使用它

let main() = 
    let inner value (next : unit -> unit)= 
     printfn "Value: %s" value 
     let input = System.Console.ReadLine() 
     if input <> "quit" then next() 
    createAliaser inner  

main() 
+0

謝謝,但我希望爲一些功能簡潔。 – Daniel 2010-05-19 21:02:49

+0

@Daniel與功能更新回答它,以及 – JaredPar 2010-05-19 21:10:08

+0

現在這很酷。我仍然沒有超過CPS的難以捉摸的地步。 – Daniel 2010-05-19 21:26:36

0

我會用Seq.unfold : (('a -> ('b * 'a) option) -> 'a -> seq<'b>)生成的別名。

實現爲:

let alias = 
    Seq.unfold (fun count -> Some(sprintf "t%i" count, count+1)) 0 
+0

除非您實施類似於我的原始帖子的內容,否則這將不會保持呼叫之間的狀態。 – Daniel 2010-05-20 14:20:57

+0

是的,但我會使用生成的序列作爲狀態。 – Huusom 2010-05-21 10:11:33