Scala Language
不変のスタイルでデータを扱う
サーチ…
備考
値と変数の名前は、より低いラクダの場合にする必要があります
定数の名前はラクダの大文字にする必要があります。つまり、メンバがfinalで不変で、パッケージオブジェクトまたはオブジェクトに属している場合は、定数と見なすことができます
メソッド、値、変数名は、ラクダの方が小さい
出典: http : //docs.scala-lang.org/style/naming-conventions.html
このコンパイル:
val (a,b) = (1,2)
// a: Int = 1
// b: Int = 2
しかしこれはしません:
val (A,B) = (1,2)
// error: not found: value A
// error: not found: value B
valとvarだけではありません
val
とvar
scala> val a = 123
a: Int = 123
scala> a = 456
<console>:8: error: reassignment to val
a = 456
scala> var b = 123
b: Int = 123
scala> b = 321
b: Int = 321
-
val
参照は変更できません:Java
final
変数のように、初期化されたら変更できません -
var
リファレンスは、Javaでの単純な変数宣言として再割り当て可能です
不変および変更可能なコレクション
val mut = scala.collection.mutable.Map.empty[String, Int]
mut += ("123" -> 123)
mut += ("456" -> 456)
mut += ("789" -> 789)
val imm = scala.collection.immutable.Map.empty[String, Int]
imm + ("123" -> 123)
imm + ("456" -> 456)
imm + ("789" -> 789)
scala> mut
Map(123 -> 123, 456 -> 456, 789 -> 789)
scala> imm
Map()
scala> imm + ("123" -> 123) + ("456" -> 456) + ("789" -> 789)
Map(123 -> 123, 456 -> 456, 789 -> 789)
Scalaの標準ライブラリは、不変のデータ構造と変更可能なデータ構造の両方を提供します。不変のデータ構造が「変更」されるたびに、元のコレクションをインプレースで変更する代わりに、新しいインスタンスが生成されます。コレクションの各インスタンスは、別のインスタンスと重要な構造を共有することができます。
しかし、私はこの場合に不変性を使用することはできません!
のは、一例として、2取る関数ピックアップしてみましょうMap
と戻りMap
のすべての要素含むma
とmb
:
def merge2Maps(ma: Map[String, Int], mb: Map[String, Int]): Map[String, Int]
最初の試みは、 for ((k, v) <- map)
を使ってマップの要素を反復し、何らかの方法でマージされたマップを返すことができます。
def merge2Maps(ma: ..., mb: ...): Map[String, Int] = {
for ((k, v) <- mb) {
???
}
}
この非常に最初の動きはすぐに制約を追加します。 その外の突然変異for
今必要とされています 。これは、以下をfor
場合にはより明確for
。
// this:
for ((k, v) <- map) { ??? }
// is equivalent to:
map.foreach { case (k, v) => ??? }
なぜ我々は突然変異を起こさなければならないのか?
foreach
は副作用に頼っています。私たちがforeach
内で何か起こることを望むたびに、 "副作用"が必要です。この場合、変数var result
を変更するか、変更可能なデータ構造を使用することができます。
result
マップの作成と塗りつぶし
のは、仮定しようma
とmb
しているscala.collection.immutable.Map
、私たちが作成することができresult
から地図をma
:
val result = mutable.Map() ++ ma
次に、要素を追加するmb
を繰り返し、 ma
の現在の要素のkey
がすでに存在する場合は、 mb
要素で上書きします。
mb.foreach { case (k, v) => result += (k -> v) }
変更可能な実装
これまでのところ非常にうまくいきました。「変更可能なコレクションを使用する必要があります。
def merge2Maps(ma: Map[String, Int], mb: Map[String, Int]): Map[String, Int] = {
val result = scala.collection.mutable.Map() ++ ma
mb.foreach { case (k, v) => result += (k -> v) }
result.toMap // to get back an immutable Map
}
予想通り:
scala> merge2Maps(Map("a" -> 11, "b" -> 12), Map("b" -> 22, "c" -> 23))
Map(a -> 11, b -> 22, c -> 23)
レスキューへの折り畳み
このシナリオでforeach
を取り除くにはどうすればよいですか?基本的には、コレクション要素を繰り返し処理して関数を適用する一方、オプションの結果を累積するには.foldLeft
を使用することができます。
def merge2Maps(ma: Map[String, Int], mb: Map[String, Int]): Map[String, Int] = {
mb.foldLeft(ma) { case (result, (k, v)) => result + (k -> v) }
// or more concisely mb.foldLeft(ma) { _ + _ }
}
この場合、私たちの "結果"は、 .foldLeft
zero
であるma
から始まる累積値.foldLeft
。
中間結果
明らかに、この不変の解決策は、折りたたみ中に多くのMap
インスタンスを生成して破壊していますが、それらのインスタンスはMap
完全なクローンではなく、既存のインスタンスと重要な構造(データ)を共有していることに言及する価値があります。
より簡単な合理性
.foldLeft
アプローチのように宣言的であれば、意味論を.foldLeft
する方が簡単です。不変のデータ構造を使用することで、実装の理解が容易になります。