Ricerca…


Osservazioni

Il valore e i nomi delle variabili dovrebbero essere nel caso di cammello inferiore

I nomi costanti dovrebbero essere nella custodia del cammello superiore. Cioè, se il membro è definitivo, immutabile e appartiene a un oggetto pacchetto o un oggetto, può essere considerato una costante

Il metodo, il valore e i nomi delle variabili dovrebbero essere in un caso di cammello inferiore

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

Questo compila:

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

ma questo non:

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

Non è solo val vs. var

val e 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 riferimenti val sono immutabili: come una variabile final in Java , una volta inizializzata non puoi cambiarla
  • var riferimenti var sono riassegnabili come una semplice dichiarazione di variabile in Java

Collezioni immutabili e mutevoli

  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)

La libreria standard di Scala offre sia strutture di dati immutabili che mutevoli, non il riferimento ad esso. Ogni volta che una struttura di dati immutabile viene "modificata", viene prodotta una nuova istanza invece di modificare la raccolta originale sul posto. Ogni istanza della raccolta può condividere una struttura significativa con un'altra istanza.

Collezione Mutevole e Immutabile (Documentazione ufficiale di Scala)

Ma non posso usare l'immutabilità in questo caso!

Prendiamo come esempio una funzione che richiede 2 Map e restituisce una Map contenente ogni elemento in ma e mb :

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

Un primo tentativo potrebbe iterare attraverso gli elementi di una delle mappe usando for ((k, v) <- map) e in qualche modo restituire la mappa unita.

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

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

}

Questa primissima mossa aggiunge immediatamente un vincolo: una mutazione al di fuori di quella for ora è necessario . Questo è più chiaro quando si deseleziona il for :

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

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

"Perché dobbiamo mutare?"

foreach si basa su effetti collaterali. Ogni volta che vogliamo che qualcosa succeda all'interno di un foreach abbiamo bisogno di "side-effect something", in questo caso potremmo mutare un var result variabile var result o possiamo usare una struttura dati mutabile.

Creazione e compilazione della mappa dei result

Supponiamo che la ma e mb siano scala.collection.immutable.Map , potremmo creare la mappa dei result da ma :

val result = mutable.Map() ++ ma

Quindi iterate tramite mb aggiungendo i suoi elementi e se la key dell'elemento corrente su ma esiste già, sovrascriviamo quella di mb .

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

Implementazione mutevole

Fin qui tutto bene, "abbiamo dovuto usare collezioni mutevoli" e una corretta implementazione potrebbe essere:

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
}

Come previsto:

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

Piegando in soccorso

Come possiamo sbarazzarci di foreach in questo scenario? Se tutto ciò che dobbiamo fare è fondamentalmente scorrere gli elementi della raccolta e applicare una funzione mentre si accumula il risultato sull'opzione potrebbe essere .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 questo caso il nostro "risultato" è il valore accumulato a partire da ma , lo zero della .foldLeft .

Risultato intermedio

Ovviamente questa soluzione immutabile sta producendo e distruggendo molte istanze di Map durante la piegatura, ma vale la pena ricordare che quelle istanze non sono un clone completo della Map accumulato ma che invece condividono una struttura significativa (dati) con l'istanza esistente.

Più ragionevole la ragionevolezza

È più facile ragionare sulla semantica se è più dichiarativa come l'approccio .foldLeft . L'utilizzo di strutture di dati immutabili potrebbe aiutare a rendere più facile ragionare la nostra implementazione.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow