F#更改对象的状态
我有一个模拟器,它用两个成员记录一些状态:一个是Class1对象,另一个是Class2对象序列 模拟运行时,将读取输入,并根据输入调用这些对象的某些方法。这种方法改变了它们的内部状态 我正在学习F#,我理解不可变数据的重要性。然而,考虑到这种状态的复杂性(比这里公开的要复杂得多),我认为拥有这种具有内部可变状态的对象并不是什么大问题。至少它是隐藏的 然而,问题是另一个。这可能很简单 在迭代之间,我丢失了对“一”和“多”对象的更改 我猜InvokeMethodOn(显然是简化的)会复制这些对象 我知道我需要一些参考资料,但是。。。我在这里有点迷路了。。。州应该有ref成员吗?InvokeMethodOn应该通过ref传递吗?都是吗?那么“多”序列呢 编辑:可能有数百万个“多”对象。它们中的每一个都有1或2kb的状态(目前只保存在一个字节块中) 编辑:将“many”更改为数组(并按照建议使用array.iter)修复了该问题。谢谢大家F#更改对象的状态,f#,pass-by-reference,immutability,record,mutable,F#,Pass By Reference,Immutability,Record,Mutable,我有一个模拟器,它用两个成员记录一些状态:一个是Class1对象,另一个是Class2对象序列 模拟运行时,将读取输入,并根据输入调用这些对象的某些方法。这种方法改变了它们的内部状态 我正在学习F#,我理解不可变数据的重要性。然而,考虑到这种状态的复杂性(比这里公开的要复杂得多),我认为拥有这种具有内部可变状态的对象并不是什么大问题。至少它是隐藏的 然而,问题是另一个。这可能很简单 在迭代之间,我丢失了对“一”和“多”对象的更改 我猜InvokeMethodOn(显然是简化的)会复制这些对象 我
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
在迭代之间,我丢失了对“一”和“多”对象的更改
我猜InvokeMethodOn(显然是简化的)会复制这些对象
你猜错了InvokeMethodOn
仅修改Class1
或Class2
的当前状态。假设每个迭代都有一个状态
记录。因为您没有在任何地方创建新的Class1
和Class2
实例,所以这些记录都指向相同的类实例,并且在每次迭代中都以相同的方式进行修改
我认为拥有这种内部状态可变的对象并不是什么大问题。至少它是隐藏的
这是一件大事。您的隐藏状态被泄漏并导致错误行为。我相信您担心的是性能,所以您希望改变Class1
和Class2
的状态。我不知道“通过推荐”能帮你什么忙。一个简单的解决方法是写
member x.InvokeMethodOn obj input =
obj.CreateNewInstanceWith input
并将while
更改为某种类型的Seq.fold
,通过调用字段上的InvokeMethodOn
返回新的状态
我认为最好是将Class1
和Class2
声明为记录,并将与块一起使用:{Class1 with value=newValue}
。如果以后需要进行性能优化,您始终可以将记录更改为具有可变字段。此外,不要将seq
声明为记录字段,这会破坏记录的结构平等性
在迭代之间,我丢失了对“一”和“多”对象的更改
我猜InvokeMethodOn(显然是简化的)会复制这些对象
你猜错了InvokeMethodOn
仅修改Class1
或Class2
的当前状态。假设每个迭代都有一个状态
记录。因为您没有在任何地方创建新的Class1
和Class2
实例,所以这些记录都指向相同的类实例,并且在每次迭代中都以相同的方式进行修改
我认为拥有这种内部状态可变的对象并不是什么大问题。至少它是隐藏的
这是一件大事。您的隐藏状态被泄漏并导致错误行为。我相信您担心的是性能,所以您希望改变Class1
和Class2
的状态。我不知道“通过推荐”能帮你什么忙。一个简单的解决方法是写
member x.InvokeMethodOn obj input =
obj.CreateNewInstanceWith input
并将while
更改为某种类型的Seq.fold
,通过调用字段上的InvokeMethodOn
返回新的状态
我认为最好是将Class1
和Class2
声明为记录,并将与块一起使用:{Class1 with value=newValue}
。如果以后需要进行性能优化,您始终可以将记录更改为具有可变字段。此外,不要将seq
声明为记录字段,它会破坏记录上的结构平等性。如果您的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
您可能想阅读本页关于
它没有什么魔力,它应该澄清很多事情
关于可变数据,还有一件事需要注意:数组在默认情况下是可变的:不需要重新声明它们是可变的。如果您的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
您可能想阅读本页关于
它没有什么魔力,它应该澄清很多事情
关于可变数据,还有一点需要注意:数组在默认情况下是可变的:不需要重新声明它们是可变的。您的ChangeInternalState
可能有问题,对于seq
你需要小心,因为seq
是懒惰的-最好使用数组/列表,而不是可能的相关问题:请参考Joh的答案。简单地说,for循环在这里会更好。如果obj.ChangeInternalState
只更改Class2