重写简单的C#嵌套类

时间:2010-05-19 20:55:57

标签: f#

在F#中实现此嵌套类的功能的优雅方法是什么?

  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

3 个答案:

答案 0 :(得分:7)

通常的写法是创建一个在闭包中捕获本地状态的函数:

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

getNextAlias的类型只是unit -> string,当你反复调用它时,它会返回字符串“t1”,“t2”,......这依赖于可变状态,但是可变状态是隐藏在用户之外。

关于你是否可以在没有可变状态的情况下做到这一点 - 简单的答案是NO,因为当你用相同的参数两次调用纯函数函数时,它必须返回相同的结果。因此,您必须使用以下结构编写内容:

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

正如您所看到的,您需要保留一些状态并通过整个代码维护它。在F#中,处理此问题的标准方法是使用可变状态。在Haskell中,您可以使用 State monad ,它允许您隐藏状态的传递。使用实现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 :(得分: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()

答案 2 :(得分:0)

我会使用Seq.unfold : (('a -> ('b * 'a) option) -> 'a -> seq<'b>)生成别名。

实施为:

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