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 enfinalvariabel iJavakan du inte ändra den när den har initialiserats -
varreferenser 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.