2

我想在 F# 对象表达式中有一个可变状态。第一种方法是使用参考单元格,如下所示:

type PP =
    abstract member A : int

let foo =
    let a = ref 0
    { new PP with
        member x.A = 
            let ret = !a
            a := !a + 1
            ret 
    }

printfn "%A" foo.A
printfn "%A" foo.A
printfn "%A" foo.A
printfn "%A" foo.A

一种不同的方法如下:

type State(s : int) =
    let mutable intState = s
    member x.state 
        with get () = intState
        and set v = intState <- v 

[<AbstractClass>]         
type PPP(state : State) =
    abstract member A : int
    member x.state 
        with get () = state.state
        and set v = state.state <- v 

let bar n =
    { new PPP(State(n)) with
        member x.A = 
            let ret = x.state
            x.state <- ret + 1
            ret  
    }

let barA1 = bar 0
printfn "%A" barA1.A
printfn "%A" barA1.A
printfn "%A" barA1.A
printfn "%A" barA1.A

哪个版本的性能可能更高(我需要在性能关键部分更新 x.state <- ret + 1 的状态)?我的猜测是 State 对象也分配在堆上,所以没有理由第二个版本应该更快。但是使用起来更有吸引力。

感谢您的任何反馈和建议

4

2 回答 2

5

正如丹尼尔所说,最后一种方法本质上等同于使用内置的ref.

使用时ref,您分配了两个对象 - 您要返回的对象和参考单元格本身。您可以通过使用具体实现将其减少为仅分配的单个对象(但我认为这在实践中并不重要):

type Stateful(initial:int) = 
  let mutable state = initial
  interface PP with
    member x.A =
      let ret = state
      state <- state + 1
      ret

let foo = 
   Statefull(0) :> PP // Creates a single object that keeps the state as mutable field

除此之外,您正在使用只读属性来修改对象的内部状态并每次返回一个新状态。这是一种危险的模式,可能会让人非常困惑 - 带有 getter 的属性不应该修改状态,因此您可能应该使用方法 ( unit -> int) 来代替。

于 2011-11-17T17:05:57.197 回答
3

您的State课程与ref. 它们都是引用类型(您不能从对象表达式中捕获可变值类型)。如果可能,我更喜欢内置类型。ref是表示堆分配的可变值的惯用方式。

如果对性能有任何疑问,请对其进行基准测试。

于 2011-11-17T16:32:20.857 回答