2014-09-03 24 views

risposta

30

Diciamo che abbiamo un Map[A,B]. Per chiarimenti: mi riferisco sempre a un Map immutabile.

mapValues prende una funzione B => C, dove C è il nuovo tipo per i valori.

transform prende una funzione (A, B) => C, dove questo C è anche il tipo per i valori.

Quindi entrambi si traducono in un Map[A,C].

Tuttavia con la funzione transform è possibile influenzare il risultato dei nuovi valori in base al valore delle proprie chiavi.

Ad esempio:

val m = Map("a" -> 2, "b" -> 3) 
m.transform((key, value) => key + value) //Map[String, String](a -> a2, b -> b3) 

Fare questo con mapValues sarà molto difficile.

La prossima differenza è che transform è rigoroso, mentre mapValues fornirà solo una vista, che non memorizzerà gli elementi aggiornati. Ecco come si presenta:

protected class MappedValues[C](f: B => C) extends AbstractMap[A, C] with DefaultMap[A, C] { 
    override def foreach[D](g: ((A, C)) => D): Unit = for ((k, v) <- self) g((k, f(v))) 
    def iterator = for ((k, v) <- self.iterator) yield (k, f(v)) 
    override def size = self.size 
    override def contains(key: A) = self.contains(key) 
    def get(key: A) = self.get(key).map(f) 
} 

(tratto da https://github.com/scala/scala/blob/v2.11.2/src/library/scala/collection/MapLike.scala#L244)

Così prestazioni-saggio dipende da cosa è più efficace. Se f è costoso e si accede solo a pochi elementi della mappa risultante, mapValues potrebbe essere migliore, poiché f viene applicato solo su richiesta. In caso contrario, attenersi a map o transform.

transform può anche essere espresso con map. Assumere m: Map[A,B] e f: (A,B) => C, quindi

m.transform(f) è equivalente a m.map{case (a, b) => (a, f(a, b))}

8

collection.Map non fornisce transform: ha una firma diversa per Maps mutabili e immutabili.

$ scala 
Welcome to Scala version 2.11.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_11). 
Type in expressions to have them evaluated. 
Type :help for more information. 

scala> val im = Map('a -> 1, 'b -> 2, 'c -> 3) 
im: scala.collection.immutable.Map[Symbol,Int] = Map('a -> 1, 'b -> 2, 'c -> 3) 

scala> im.mapValues(_ * 7) eq im 
res0: Boolean = false 

scala> im.transform { case (k,v) => v*7 } eq im 
res2: Boolean = false 

scala> val mm = collection.mutable.Map('a -> 1, 'b -> 2, 'c -> 3) 
mm: scala.collection.mutable.Map[Symbol,Int] = Map('b -> 2, 'a -> 1, 'c -> 3) 

scala> mm.mapValues(_ * 7) eq mm 
res3: Boolean = false 

scala> mm.transform { case (k,v) => v*7 } eq mm 
res5: Boolean = true 

Mutevole trasformare muta in atto:

scala> mm.transform { case (k,v) => v*7 } 
res6: mm.type = Map('b -> 98, 'a -> 49, 'c -> 147) 

scala> mm.transform { case (k,v) => v*7 } 
res7: mm.type = Map('b -> 686, 'a -> 343, 'c -> 1029) 

Così mutevole trasformare non cambia il tipo di mappa:

scala> im mapValues (_ => "hi") 
res12: scala.collection.immutable.Map[Symbol,String] = Map('a -> hi, 'b -> hi, 'c -> hi) 

scala> mm mapValues (_ => "hi") 
res13: scala.collection.Map[Symbol,String] = Map('b -> hi, 'a -> hi, 'c -> hi) 

scala> mm.transform { case (k,v) => "hi" } 
<console>:9: error: type mismatch; 
found : String("hi") 
required: Int 
       mm.transform { case (k,v) => "hi" } 
             ^

scala> im.transform { case (k,v) => "hi" } 
res15: scala.collection.immutable.Map[Symbol,String] = Map('a -> hi, 'b -> hi, 'c -> hi) 

... come può accadere quando si costruisce una nuova carta geografica.

2

Ecco un paio di differenze unmentioned:

  • mapValues crea una mappa che non è serializzabile, senza alcuna indicazione che è solo una vista (il tipo è Map[_, _], ma solo cercare di inviare uno attraverso il filo).

  • Da mapValues è solo una vista, ogni istanza contiene il vero Map - che potrebbe essere un altro risultato di mapValues. Immagina di avere un attore con uno stato, e ogni mutazione dello stato imposta il nuovo stato come mapValues sullo stato precedente ... alla fine hai mappe profondamente annidate con una copia di ogni stato precedente dell'attore (e , sì, entrambi provengono dall'esperienza).