Scala Language
Praca z danymi w niezmiennym stylu
Szukaj…
Uwagi
Wartości i nazwy zmiennych powinny być pisane małymi literami wielbłąda
Nazwy stałe powinny być pisane wielkimi literami wielbłąda. Oznacza to, że jeśli element jest ostateczny, niezmienny i należy do obiektu pakietu lub obiektu, można go uznać za stały
Nazwy metod, wartości i zmiennych powinny być pisane małymi literami wielbłąda
Źródło: http://docs.scala-lang.org/style/naming-conventions.html
Ta kompilacja:
val (a,b) = (1,2)
// a: Int = 1
// b: Int = 2
ale to nie:
val (A,B) = (1,2)
// error: not found: value A
// error: not found: value B
To nie tylko val vs. var
val
i 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
- referencje
val
są niezmienne: jakfinal
zmienna wJava
, po jej zainicjowaniu nie można jej zmienić -
var
referencje są ponownie przypisać jako deklaracja zmiennej prosty w Javie
Kolekcje niezmienne i zmienne
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)
Standardowa biblioteka Scala oferuje zarówno niezmienne, jak i zmienne struktury danych, a nie ich odniesienia. Za każdym razem, gdy niezmienna struktura danych zostaje „zmodyfikowana”, tworzona jest nowa instancja zamiast modyfikować oryginalny zbiór w miejscu. Każde wystąpienie kolekcji może mieć znaczącą strukturę z innym wystąpieniem.
Zmienna i niezmienna kolekcja (oficjalna dokumentacja Scala)
Ale w tym przypadku nie mogę użyć niezmienności!
Wybierzmy przykładowo funkcję, która pobiera 2 Map
i zwraca Map
zawierającą każdy element w ma
i mb
:
def merge2Maps(ma: Map[String, Int], mb: Map[String, Int]): Map[String, Int]
Pierwszą próbą może być iteracja elementów jednej z map za pomocą for ((k, v) <- map)
i jakoś zwrócić scaloną mapę.
def merge2Maps(ma: ..., mb: ...): Map[String, Int] = {
for ((k, v) <- mb) {
???
}
}
Ten pierwszy ruch natychmiast dodaje ograniczenie: potrzebna jest teraz mutacja poza tym for
. Jest to wyraźniejsze, gdy odsłaniasz for
:
// this:
for ((k, v) <- map) { ??? }
// is equivalent to:
map.foreach { case (k, v) => ??? }
„Dlaczego musimy mutować?”
foreach
opiera się na skutkach ubocznych. Za każdym razem, gdy chcemy, aby coś się wydarzyło w obrębie foreach
, musimy „coś var result
efektem ubocznym”, w tym przypadku moglibyśmy zmutować zmienny var result
lub możemy zastosować zmienną strukturę danych.
Tworzenie i wypełnianie mapy result
Załóżmy, że ma
i mb
to scala.collection.immutable.Map
, możemy utworzyć result
Map z ma
:
val result = mutable.Map() ++ ma
Następnie iteruj przez mb
dodając jego elementy i jeśli key
bieżącego elementu na ma
już istnieje, zastąpmy go mb
.
mb.foreach { case (k, v) => result += (k -> v) }
Zmienna implementacja
Jak dotąd tak dobrze, „musieliśmy używać zmiennych zbiorów”, a poprawną implementacją może być:
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
}
Zgodnie z oczekiwaniami:
scala> merge2Maps(Map("a" -> 11, "b" -> 12), Map("b" -> 22, "c" -> 23))
Map(a -> 11, b -> 22, c -> 23)
Składany na ratunek
Jak możemy pozbyć się foreach
w tym scenariuszu? Jeśli wszystko, co należy zrobić, to w zasadzie iterować elementy kolekcji i zastosować funkcję, kumulując wynik opcji, można użyć .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) { _ + _ }
}
W tym przypadku naszym „wynikiem” jest skumulowana wartość zaczynająca się od ma
, zero
z .foldLeft
.
Wynik pośredni
Oczywiście to niezmienne rozwiązanie powoduje i niszczy wiele instancji Map
podczas składania, ale warto wspomnieć, że te instancje nie są pełnym klonem zgromadzonej Map
ale zamiast tego dzielą znaczącą strukturę (dane) z istniejącą instancją.
Łatwiejsza racjonalność
Łatwiej jest wnioskować o semantyce, jeśli jest ona bardziej deklaratywna jak podejście .foldLeft
. Korzystanie z niezmiennych struktur danych może pomóc w usprawnieniu naszej implementacji.