我有一个模拟器,它将一些状态保存为具有两个成员的记录:Class1的一个对象和Class2的一系列对象。
当模拟运行时,读取输入,并根据输入调用这些对象的某些方法。这种方法改变了他们的内部状态。
我正在学习F#,我理解不可变数据的重要性。但是,考虑到这种状态的复杂性(远远超过这里暴露),我认为拥有内部可变状态的这些对象并不是什么大问题。至少它是隐藏的。
然而,问题是另一个问题。它可能很简单。在迭代之间,我失去了对“一个”和“多个”对象的更改!
我猜InvokeMethodOn(显然是简化的)会获取这些对象的副本。
我意识到我需要某种参考,但是......我在这里有点迷失......国家应该有参与成员吗? InvokeMethodOn应该通过ref传递?他们都是?那么“很多”的序列呢?
编辑:可能有数百万“很多”对象。它们每个都有1或2 KB的状态(现在只保留在一个字节的blob中)。
编辑:将“many”更改为数组(并按建议使用Array.iter)修复了该问题。谢谢大家!
type State = {
one : Class1
many : Class2 seq
}
type Simulator() = class
member x.run(state : State) =
// ....
while ...
let input = ReadInput
if someFuncOf(input)
then InvokeMethodOn(state.one, input)
else Seq.iter (fun x -> InvokeMethodOn(x, input)) state.many
member x.InvokeMethodOn obj input =
obj.ChangeInternalState input
答案 0 :(得分:2)
你猜错了;在迭代之间,我失去了对“一个”和“多个”对象的更改!
我猜InvokeMethodOn(显然是简化的)会获取这些对象的副本。
InvokeMethodOn
仅修改Class1
或Class2
的当前状态。假设您每次迭代都有State
条记录。因为您没有在任何地方创建新的Class1
和Class2
实例,所以这些记录都指向相同的类实例,并且在每次迭代中都以相同的方式进行修改。
我认为拥有内部可变状态的对象并不是什么大问题。至少它是隐藏的。
这是一个大问题。您的隐藏状态被泄露并导致错误行为。我相信你担心性能,所以你想改变Class1
和Class2
的状态。我不知道参考传递如何帮助你。一种简单的解决方法是编写
member x.InvokeMethodOn obj input =
obj.CreateNewInstanceWith input
并通过在字段上调用while
,将Seq.fold
更改为某种State
,其中您返回新的InvokeMethodOn
。
我认为如果您将Class1
和Class2
声明为记录并使用with
阻止{class1 with value = newValue}
会更好。如果需要进行性能优化,您总是可以在以后更改记录以获得可变字段。此外,不要将seq
声明为记录字段,它会破坏记录中的结构相等。
答案 1 :(得分:2)
如果您的Class1和Class2包含您更改的可变状态,我没有看到为什么在每次迭代时都会丢弃此类更改的原因,除非您以某种方式重新创建Class1的新副本。
如果我尝试编写与所呈现类似的东西的脚本,它会运行罚款。 知道你的代码如何偏离以找到我们遗漏的东西会很有趣。
type Class1 = { mutable label : string}
type Container = { one : Class1; many : Class1 seq}
let a = { label = "a" }
let bs = [ { label = "b1" } ; { label = "b2" }]
let cont = { one =a ; many = bs}
printfn "%A" cont.one.label
cont.one.label <- "changed a"
cont.many |> Seq.iter (fun x -> x.label <- "changed b")
printfn "%A" cont
cont.one.label <- "changed again a"
printfn "%A" cont
请注意,在F#ref中,实际上只是“可变内容”的隐藏表示
type 'a ref = { mutable contents : 'a }
您可能希望阅读有关mutation in FSharp的此页面 它没什么吸引力,应该澄清很多东西。
关于可变数据还有一点需要注意的是,默认情况下,数组是可变的:不需要重新声明它们是可变的。