F#:変更可能vs.参照


82

まず、この質問が重複している可能性があることを認めます。私に知らせて。

可変性が必要な状況での一般的な「ベストプラクティス」が何であるかを知りたいです。F#は、このための2つの機能を提供let mutableしているようです。「ほとんどの」言語の変数のように機能するように見えるバインディングと、ref使用するために明示的な逆参照を必要とする参照セル(関数で作成)です。

どちらか一方に「強制」されるケースがいくつかあります。.NET相互運用機能はで可変を使用する傾向が<-あり、ワークフロー計算refでは:=。で使用する必要があります。したがって、これらのケースはかなり明確ですが、これらのシナリオの外で独自の可変変数を作成するときに何をすべきか興味があります。あるスタイルが他のスタイルよりも優れている点は何ですか?(おそらく、実装についてさらに洞察することが役立つでしょう。)

ありがとう!



4
F#バージョン4では、以前は参照が必要だった場所で可変を使用できることに注意してください。 blogs.msdn.com/b/fsharpteam/archive/2014/11/12/…–
James Moore

回答:


133

私はgradbotが言ったことをサポートすることしかできません-突然変異が必要なとき、私は好みlet mutableます。

実装と2つの違いに関して-refセルは基本的に、可変レコードフィールドを含む非常に単純なレコードによって実装されます。あなたはそれらを自分で簡単に書くことができます:

type ref<'T> =  // '
  { mutable value : 'T } // '

// the ref function, ! and := operators look like this:
let (!) (a:ref<_>) = a.value
let (:=) (a:ref<_>) v = a.value <- v
let ref v = { value = v }

2つのアプローチの顕著な違いは、let mutable可変値をスタックに(C#の可変変数として)ref格納する一方で、ヒープに割り当てられたレコードのフィールドに可変値を格納することです。これはパフォーマンスにいくらかの影響を与えるかもしれませんが、私には数字がありません...

このおかげで、を使用refする可変値にエイリアスを設定できます。つまり、同じ可変値を参照する2つの値を作成できます。

let a = ref 5  // allocates a new record on the heap
let b = a      // b references the same record
b := 10        // modifies the value of 'a' as well!

let mutable a = 5 // mutable value on the stack
let mutable b = a // new mutable value initialized to current value of 'a'
b <- 10           // modifies the value of 'b' only!

2
念のために言っておきますが、スタックまたはヒープ上にあることは実装の詳細であり、質問とは正確には関連していません(ただし、それでも優れた回答です)
Bruno Brant

5
何かがヒープの割り当てと収集のオーバーヘッドを招くかどうかを知ることは、ベストプラクティスが何であるかを決定する際に非常に重要であると私は主張します。
jackmott 2016

@jackmottは、EricLippertによるTheStack Is An Implementation Detail
jaromey 2018

5
@jaromeyはい、私はその点でエリックに根本的に同意しないことを理解しています。「ベストプラクティス」とは何かを検討するときに、パフォーマンスの考慮事項を脇に置くことはできません。多くの場合、パフォーマンスは重要ではないと言われていますが、多くのソフトウェアは、数千の実装の詳細による死のために遅いです。
jackmott 2018

18

関連する質問:「ローカルの可変値はクロージャでキャプチャできないため、代わりにrefを使用する必要があるとおっしゃいました。これは、クロージャでキャプチャされた可変値をヒープに割り当てる必要があるためです(クロージャはに割り当てられるため)ヒープ)。」F#からref-mutable vars vs object fields

let mutable参照セルよりも好ましいと思います。私は個人的に、必要な場合にのみ参照セルを使用します。

私が書いたほとんどのコードは、再帰呼び出しと末尾呼び出しのおかげで、可変変数を使用していません。可変データのグループがある場合は、レコードを使用します。let mutableプライベートな可変変数を作成するために使用するオブジェクトの場合。私は実際には、クロージャー、通常はイベントにのみ参照セルを使用します。



4

ブライアンによるこの記事は答えを提供するかもしれません。

ミュータブルは使いやすく効率的です(ラッピングなし)が、ラムダでキャプチャすることはできません。Refセルキャプチャできますが、冗長で効率が低くなります(?-これはわかりません)。


「(...)そして効率が悪い」-おそらく、ラッパー型はより多くのメモリを必要とします。
JMCF125 2013

2
F#4.0ミュータブルはほとんどの場合キャプチャでき、refの必要性がはるかに少なくなったため、これは変更されました。
アベル

3

ウィキブックスの「可変データ」セクションを確認することをお勧めします。

便宜上、ここにいくつかの関連する引用があります:

mutableキーワードは、可変レコードを作成するためにレコードタイプで頻繁に使用されます

可変変数は多少制限されています。可変変数は、それらが定義されている関数のスコープ外ではアクセスできません。具体的には、これは、別の関数のサブ関数でミュータブルを参照できないことを意味します。

Refセルは、可変の制限のいくつかを回避します。実際、refセルは非常に単純なデータ型であり、レコード型の可変フィールドをラップします。

参照セルはヒープに割り当てられるため、複数の関数間で共有できます

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.