可変コレクションでvalを使用する場合と、不変コレクションでvarを使用する場合について、Scalaにガイドラインはありますか?それとも、不変のコレクションでvalを本当に目指すべきでしょうか?
両方の種類のコレクションがあるという事実は、私に多くの選択肢を与えてくれます、そして私はしばしばその選択をする方法を知りません。
可変コレクションでvalを使用する場合と、不変コレクションでvarを使用する場合について、Scalaにガイドラインはありますか?それとも、不変のコレクションでvalを本当に目指すべきでしょうか?
両方の種類のコレクションがあるという事実は、私に多くの選択肢を与えてくれます、そして私はしばしばその選択をする方法を知りません。
回答:
これはかなりよくある質問です。難しいのは重複を見つけることです。
参照の透明性に努めるべきです。私は表現「e」を持っている場合の手段がそのあることを、私が作ることができるval x = e
、と交換してくださいe
とx
。これは、可変性が壊れる性質です。設計上の決定を行う必要がある場合は常に、参照の透明性を最大化してください。
実際問題として、メソッドローカルvar
はvar
メソッドをエスケープしないため、最も安全です。メソッドが短い場合はさらに良いです。そうでない場合は、他のメソッドを抽出して削減してみてください。
一方、可変コレクションは、逃げなくても逃げる可能性があります。コードを変更するときに、他のメソッドに渡したり、コードを返したりしたい場合があります。それは、参照の透明性を壊すようなものです。
オブジェクト(フィールド)でもほぼ同じことが起こりますが、より悲惨な結果が生じます。どちらの方法でもオブジェクトは状態を持つため、参照の透明性が失われます。ただし、変更可能なコレクションを使用すると、オブジェクト自体でさえ、誰が変更しているかを制御できなくなる可能性があります。
immutable val
オーバーimmutable var
の上にmutable val
オーバーmutable var
。特にimmutable var
上mutable val
!
var
。不変コレクションを使用するもう1つの優れた機能は、var
変更が発生した場合でも、古いコピーを効率的に保持できることです。
var x: Set[Int]
し val x: mutable.Set[Int]
てください。x
x
不変コレクションを使用していて、それらを「変更」する必要がある場合、たとえば、ループに要素を追加var
する場合、結果のコレクションをどこかに格納する必要があるため、s を使用する必要があります。不変コレクションのみを読み取る場合は、val
s を使用します。
一般に、参照とオブジェクトを混同しないようにしてください。val
sは不変の参照(Cの定数ポインター)です。つまり、を使用するとval x = new MutableFoo()
、ポイントするオブジェクトを変更できますが、x
ポイントするオブジェクトを変更する ことはできませんx
。を使用すると、逆のことが成り立ちますvar x = new ImmutableFoo()
。最初のアドバイスを取り上げますval
。参照ポイントをどのオブジェクトに変更する必要がない場合は、sを使用します。
var immutable = something(); immutable = immutable.update(x)
不変のコレクションを使用する目的を無効にします。参照の透過性はすでにあきらめており、通常は時間の複雑さが増すミュータブルコレクションから同じ効果を得ることができます。4つの可能性(val
およびvar
、ミュータブルおよびイミュータブル)のうち、これは最も意味がありません。私はよく使いますval mutable
。
var list: List[X] = Nil; list = item :: list; ...
以前は別の方法で書いたことを忘れていました。
これに答える最良の方法は、例を使用することです。何らかの理由で単純に数値を収集するプロセスがあるとします。これらの番号をログに記録し、コレクションを別のプロセスに送信してこれを実行します。
もちろん、ロガーにコレクションを送信した後も、まだ番号を収集しています。また、実際のロギングを遅らせるロギングプロセスのオーバーヘッドがあるとします。うまくいけば、これがどこに向かっているのかがわかるでしょう。
このコレクションをミュータブルval
(継続的に追加しているためミュータブル)に保存する場合、これは、ロギングを実行するプロセスが、コレクションプロセスによってまだ更新されている同じオブジェクトを参照することを意味します。そのコレクションはいつでも更新される可能性があるため、ログを記録するときは、実際に送信したコレクションをログに記録していない可能性があります。
immutableを使用する場合var
、ロガーに不変のデータ構造を送信します。コレクションにさらに数値を追加すると、新しい不変データ構造に置き換えられます。これは、ロガーに送信されたコレクションが置き換えられるという意味ではありません。送信されたコレクションをまだ参照しています。したがって、ロガーは実際に受信したコレクションをログに記録します。var
var immutable
対 val mutable
この質問に対する多くの優れた回答に加えて。の潜在的な危険性を示す簡単な例を次に示しval mutable
ます。
可変オブジェクトは、メソッド内で変更することができ、それらをパラメーターとして受け取りますが、再割り当ては許可されません。
import scala.collection.mutable.ArrayBuffer
object MyObject {
def main(args: Array[String]) {
val a = ArrayBuffer(1,2,3,4)
silly(a)
println(a) // a has been modified here
}
def silly(a: ArrayBuffer[Int]): Unit = {
a += 10
println(s"length: ${a.length}")
}
}
結果:
length: 5
ArrayBuffer(1, 2, 3, 4, 10)
var immutable
再割り当てが許可されていないため、このようなことがで発生することはありません。
object MyObject {
def main(args: Array[String]) {
var v = Vector(1,2,3,4)
silly(v)
println(v)
}
def silly(v: Vector[Int]): Unit = {
v = v :+ 10 // This line is not valid
println(s"length of v: ${v.length}")
}
}
結果:
error: reassignment to val
関数パラメーターはval
このように扱われるため、再割り当てはできません。
mutable val
していimmutable var
ます。これは、最初の例の動作がで不可能であることを示すためです。ここで何が間違っていますか?
+=
配列バッファのようなメソッドはありません。あなたの答え+=
はx = x + y
それがそうでないのと同じであることを意味します。関数paramsがvalsとして扱われるというあなたの声明は正しいです、そしてあなたはあなたが言及したエラーを受け取りますが、あなたが使用し=
たからです。ArrayBufferでも同じエラーが発生する可能性があるため、ここでのコレクションの可変性はあまり関係ありません。OPが話していることを理解していないので、それは良い答えではありません。これは、意図しない場合に可変コレクションを渡す危険の良い例です。
ArrayBuffer
、を使用して、で得られる動作を再現することはできませんVector
。OPの質問は広範囲ですが、彼らはいつ使用するかについての提案を探していたので、可変コレクションを渡すことの危険性を示しているので、私の答えは有用だと思います(役に立たval
ないという事実)。immutable var
より安全ですmutable val
。