2010-05-19 56 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