Suche…


Bemerkungen

Wert- und Variablennamen sollten im unteren Kamelfall stehen

Konstante Namen sollten im oberen Kamel sein. Wenn das Member also final ist, unveränderlich ist und zu einem Paketobjekt oder einem Objekt gehört, kann es als Konstante betrachtet werden

Methoden-, Wert- und Variablennamen sollten in Kleinbuchstaben stehen

Quelle: http://docs.scala-lang.org/style/naming-conventions.html

Dieses kompilieren:

val (a,b) = (1,2)
// a: Int = 1
// b: Int = 2

aber das tut nicht:

val (A,B) = (1,2)
// error: not found: value A
// error: not found: value B

Es ist nicht nur val vs. var

val und 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 Verweise sind nicht veränderbar: Wie eine final Variable in Java kann sie nach der Initialisierung nicht mehr geändert werden
  • var Referenzen können als einfache Variablendeklaration in Java erneut zugewiesen werden

Unveränderliche und veränderliche Sammlungen

  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)

Die Standardbibliothek von Scala bietet sowohl unveränderliche als auch veränderliche Datenstrukturen, nicht den Verweis darauf. Jedes Mal, wenn eine unveränderliche Datenstruktur "geändert" wird, wird eine neue Instanz erstellt, anstatt die ursprüngliche Sammlung direkt zu ändern. Jede Instanz der Sammlung kann eine signifikante Struktur mit einer anderen Instanz gemeinsam nutzen.

Veränderliche und unveränderliche Sammlung (offizielle Scala-Dokumentation)

Aber ich kann in diesem Fall keine Unveränderlichkeit verwenden!

Nehmen wir als Beispiel eine Funktion, die 2 Map und gibt eine Map die jedes Element in ma und mb :

def merge2Maps(ma: Map[String, Int], mb: Map[String, Int]): Map[String, Int]

Ein erster Versuch könnte darin bestehen, die Elemente einer der Karten mit for ((k, v) <- map) zu durchlaufen und die zusammengeführte Karte irgendwie zurückzugeben.

def merge2Maps(ma: ..., mb: ...): Map[String, Int] = {

  for ((k, v) <- mb) {
    ???
  }

}

Dieser allererste Schritt fügt sofort eine Einschränkung hinzu: Jetzt wird eine Mutation außerhalb von for benötigt . Dies ist deutlicher beim Entzuckern for :

// this:
for ((k, v) <- map) { ??? }

// is equivalent to:
map.foreach { case (k, v) => ??? }

"Warum müssen wir mutieren?"

foreach stützt sich auf Nebenwirkungen. Jedes Mal, wenn wir möchten, dass etwas innerhalb eines foreach geschieht, müssen wir etwas "Nebeneffekt" haben. In diesem Fall könnten wir ein variables var result ändern oder eine veränderliche Datenstruktur verwenden.

Erstellen und Füllen der result

Nehmen wir an, ma und mb sind scala.collection.immutable.Map , wir könnten das result Map aus ma erstellen:

val result = mutable.Map() ++ ma

Iterieren dann durch mb seine Elemente hinzuzufügen und wenn der key des aktuellen Elements auf ma bereits vorhanden ist , machen sie es mit dem außer Kraft setzen mb ein.

mb.foreach { case (k, v) => result += (k -> v) }

Veränderliche Implementierung

So weit so gut, wir mussten "veränderliche Sammlungen verwenden" und eine korrekte Implementierung könnte sein:

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
}

Wie erwartet:

scala> merge2Maps(Map("a" -> 11, "b" -> 12), Map("b" -> 22, "c" -> 23))
  Map(a -> 11, b -> 22, c -> 23)

Zur Rettung falten

Wie können wir in diesem Szenario foreach loswerden? Wenn wir im Grunde nur die Collection-Elemente .foldLeft und eine Funktion anwenden, während das Ergebnis mit der Option akkumuliert wird, könnte .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) { _ + _ }
}

In diesem Fall ist unser "Ergebnis" der akkumulierte Wert ausgehend von ma , der zero von .foldLeft .

Zwischenergebnis

Offensichtlich produziert und zerstört diese unveränderliche Lösung viele Map Instanzen beim Falten, aber es ist erwähnenswert, dass diese Instanzen kein vollständiger Klon der Map , sondern eine signifikante Struktur (Daten) mit der vorhandenen Instanz gemeinsam nutzen.

Einfachere Angemessenheit

Es ist einfacher, über die Semantik zu .foldLeft wenn sie deklarativer ist als der .foldLeft Ansatz. Die Verwendung unveränderlicher Datenstrukturen könnte dazu beitragen, die Implementierung der Implementierung zu erleichtern.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow