Scala Language
Arbetar med data i oföränderlig stil
Sök…
Anmärkningar
Värdet och variabla namn bör vara i mindre kamelfall
Ständiga namn bör vara i övre kamelfall. Det vill säga, om medlemmen är slutlig, oföränderlig och den tillhör ett paketobjekt eller ett objekt, kan det betraktas som en konstant
Metod, värde och variabla namn ska vara i mindre kamelfall
Källa: http://docs.scala-lang.org/style/naming-conventions.html
Denna sammanställning:
val (a,b) = (1,2)
// a: Int = 1
// b: Int = 2
men detta gör inte:
val (A,B) = (1,2)
// error: not found: value A
// error: not found: value B
Det är inte bara val kontra var
val
och 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
är oföränderliga: som enfinal
variabel iJava
kan du inte ändra den när den har initialiserats -
var
referenser kan överföras som en enkel variabel deklaration i Java
Oändliga och muterbara samlingar
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 standardbibliotek erbjuder både immutable och mutable datastrukturer, inte referensen till det. Varje gång en immutable datastruktur "modifieras" produceras en ny instans istället för att modifiera den ursprungliga samlingen på plats. Varje instans av samlingen kan dela betydande struktur med en annan instans.
Muterbar och oföränderlig samling (officiell Scala-dokumentation)
Men jag kan inte använda oföränderlighet i det här fallet!
Låt oss välja som exempel en funktion som tar 2 Map
och returnerar en Map
innehåller alla element i ma
och mb
:
def merge2Maps(ma: Map[String, Int], mb: Map[String, Int]): Map[String, Int]
Ett första försök kan vara att iterera genom elementen på en av kartorna med for ((k, v) <- map)
och på något sätt returnera den sammanslagna kartan.
def merge2Maps(ma: ..., mb: ...): Map[String, Int] = {
for ((k, v) <- mb) {
???
}
}
Detta allra första drag lägger omedelbart en begränsning: en mutation utanför det for
nu behövs . Detta är mer tydligt när av-socker av for
:
// this:
for ((k, v) <- map) { ??? }
// is equivalent to:
map.foreach { case (k, v) => ??? }
"Varför måste vi mutera?"
foreach
beror på biverkningar. Varje gång vi vill att något ska hända inom en foreach
måste vi " foreach
något", i det här fallet kan vi mutera ett variabelt var result
eller så kan vi använda en muterbar datastruktur.
Skapa och fylla result
Låt oss anta att ma
och mb
är scala.collection.immutable.Map
, vi kan skapa result
Map från ma
:
val result = mutable.Map() ++ ma
Sedan iterera igenom mb
lägga till dess element och om key
för det aktuella elementet på ma
redan finns, låt oss åsidosätta det med mb
ett.
mb.foreach { case (k, v) => result += (k -> v) }
Muterbart genomförande
Hittills så bra, vi "var tvungna att använda muterbara samlingar" och en korrekt implementering kan vara:
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
}
Som förväntat:
scala> merge2Maps(Map("a" -> 11, "b" -> 12), Map("b" -> 22, "c" -> 23))
Map(a -> 11, b -> 22, c -> 23)
Fälls till räddningen
Hur kan vi bli av med foreach
i det här scenariot? Om allt vi ska göra är att iterera över samlingselementen och tillämpa en funktion medan du samlar resultatet på alternativet kan använda .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) { _ + _ }
}
I detta fall vår "resultat" är det ackumulerade värdet med utgångspunkt från ma
, det zero
i .foldLeft
.
Mellanresultat
Det är uppenbart att denna immutabla lösning producerar och förstör många Map
under vikning, men det är värt att nämna att dessa instanser inte är en fullständig klon av den samlade Map
utan istället delar betydande struktur (data) med den existerande instansen.
Enklare rimlighet
Det är lättare att resonera om det semantiska om det är mer deklarativt som .foldLeft
metoden. Att använda immutable datastrukturer kan hjälpa till att göra vår implementering lättare att resonera med.