1

クラス 1 の 1 つのオブジェクトとクラス 2 のオブジェクトのシーケンスの 2 つのメンバーを持つレコードとしていくつかの状態を保持するシミュレーターがあります。

シミュレーションが実行されると、入力が読み取られ、入力に応じて、それらのオブジェクトのいくつかのメソッドが呼び出されます。このようなメソッドは、内部状態を変更します。

私は F# を学んでおり、不変データの重要性を理解しています。ただし、そのような状態の複雑さ (ここで公開したものよりもはるかに多い) を考えると、そのようなオブジェクトに内部可変状態を持たせることは大したことではないと思います。少なくともそれは隠されています。

ただし、問題はもう 1 つあります。そして、それはおそらく単純です。

反復の間に、「1 つ」および「多数」のオブジェクトへの変更が失われます。

InvokeMethodOn (明らかに単純化されています) は、これらのオブジェクトのコピーを取ると思います。

ここである種の参照が必要なことはわかっていますが...ここで少し迷っています...状態には参照メンバーが必要ですか? InvokeMethodOn は ref で渡す必要がありますか? それらのすべて?そして、「多くの」シーケンスはどうですか?

編集:何百万もの「多くの」オブジェクトが存在する可能性があります。それらのそれぞれには、1 または 2 KB の状態があります (現時点では、バイトの塊に保持されています)。

編集:「多く」を配列に変更する(および提案されているように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
4

2 に答える 2

2

反復の間に、「1 つ」および「多数」のオブジェクトへの変更が失われます。

InvokeMethodOn (明らかに単純化されています) は、これらのオブジェクトのコピーを取ると思います。

あなたの推測は間違っていました。またはInvokeMethodOnの現在の状態のみを変更します。反復ごとにレコードがあるとしましょう。新しいインスタンスとインスタンスをどこにも作成しなかったため、これらのレコードはすべて同じクラス インスタンスを指し、各反復ですべて同じ方法で変更されます。Class1Class2StateClass1Class2

このようなオブジェクトを内部で可変状態にすることは大したことではないと思います。少なくともそれは隠されています。

それは大したことです。非表示の状態が漏洩し、間違った動作を引き起こします。Class1との状態を変更したいので、パフォーマンスを心配していると思いますClass2。参照渡しがどのように役立つかわかりません。修正する簡単な方法は、次のように書くことです

member x.InvokeMethodOn obj input =
         obj.CreateNewInstanceWith input

フィールドを呼び出してnew を返すような場所に変更whileします。Seq.foldStateInvokeMethodOn

Class1andClass2をレコードとして宣言してwithblock:を使った方が良いと思います{class1 with value = newValue}。パフォーマンスの最適化を行う必要がある場合は、いつでも変更可能なフィールドを持つようにレコードを変更できます。seqさらに、レコード フィールドとして宣言しないでください。レコードの構造的等価性が破壊されます。

于 2012-10-01T06:20:57.303 に答える
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 }

FSharp のミューテーションに関するこのページをお読みになることをお勧めします 。

また、変更可能なデータに関して注意すべきことの 1 つは、配列がデフォルトで変更可能であることです。変更可能であることを再宣言する必要はありません。

于 2012-10-01T08:28:39.677 に答える