在F#中实现这个嵌套类的功能的优雅方法是什么?
private class Aliaser {
private int _count;
internal Aliaser() { }
internal string GetNextAlias() {
return "t" + (_count++).ToString();
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的第一次尝试,但感觉应该有一个性感的单行为此:
let aliases = (Seq.initInfinite (sprintf "t%d")).GetEnumerator()
let getNextAlias() =
aliases.MoveNext() |> ignore
aliases.Current
Run Code Online (Sandbox Code Playgroud)
通常的编写方式是创建一个在闭包中捕获的本地状态的函数:
let getNextAlias =
let count = ref 0
(fun () ->
count := !count + 1;
sprintf "t%d" (!count))
Run Code Online (Sandbox Code Playgroud)
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
// ...
Run Code Online (Sandbox Code Playgroud)
如您所见,您需要保留一些状态并通过整个代码维护它.在F#中,处理此问题的标准方法是使用可变状态.在Haskell中,您可以使用State monad,它允许您隐藏状态的传递.使用此问题的实现,您可以编写如下内容:
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
Run Code Online (Sandbox Code Playgroud)
这非常类似于其他计算,例如,lazy或者seq实际上 - state { .. }块中的计算具有某种状态,您可以通过提供状态的初始值来执行它们.但是,除非你有充分的理由要求纯功能解决方案,否则我更喜欢第一个版本用于实际的F#编程.