2012-01-11 5 views
5

chcę osiągnąć coś jak poniżej:mapie pojedynczy wpis z mapy

(_ : Map[K,Int]).mapKey(k, _ + 1) 

a funkcja mapKey stosuje swój drugi argument (Int => Int) tylko do wartości przechowywanej pod k. Czy jest coś w standardowej lib? Jeśli nie, to założę się, że jest coś w Scalaz.

Oczywiście mogę napisać tę funkcję samodzielnie (m.updated(k,f(m(k))) i jest to proste. Ale kilka razy doszłam do tego problemu, więc może już to zrobione?

Dla Scalaz sobie wyobrazić coś w poniższym kodzie:

(m: Map[A,B]).project(k: A).map(f: B => B): Map[A,B] 
+0

Czego chcesz się zdarzyć, jeśli 'A' klucz nie jest na mapie? Nic nie jest w porządku? Pytam, ponieważ 'updated' wstawi' (a -> f (m (a)) '... – huynhjl

+0

@huynhjl Dla mojego przypadku użycia mam mapę z wartością domyślną. Myślę, że wydaje się bardziej naturalne nic się nie dzieje, zauważ, że moje proste rozwiązania zawiodą z wyjątkiem. – ziggystar

Odpowiedz

3

Można by oczywiście dodać

def changeForKey[A,B](a: A, fun: B => B): Tuple2[A, B] => Tuple2[A, B] = { kv => 
    kv match { 
    case (`a`, b) => (a, fun(b)) 
    case x => x 
    } 
} 

val theMap = Map('a -> 1, 'b -> 2) 
theMap map changeForKey('a, (_: Int) + 1) 
res0: scala.collection.immutable.Map[Symbol,Int] = Map('a -> 2, 'b -> 2) 

Ale byłoby to obejście żadnej optymalizacji pamięci dotyczącej ponownego użycia i dostępu.

doszedłem również z dość gadatliwym i nieefektywnego rozwiązania scalaz wykorzystaniem zamek dla proponowanej project metody:

theMap.toStream.toZipper.flatMap(_.findZ(_._1 == 'a).flatMap(elem => elem.delete.map(_.insert((elem.focus._1, fun(elem.focus._2)))))).map(_.toStream.toMap) 

lub

(for { 
    z <- theMap.toStream.toZipper 
    elem <- z.findZ(_._1 == 'a) 
    z2 <- elem.delete 
} yield z2.insert((elem.focus._1, fun(elem.focus._2)))).map(_.toStream.toMap) 

Prawdopodobnie mało użyteczne. Po prostu publikuję dla odniesienia.

1

Można pimp ją to tak, że tworzy nową mapę na podstawie starego:

class MapUtils[A, B](map: Map[A, B]) { 
    def mapValueAt(a: A)(f: (B) => B) = map.get(a) match { 
    case Some(b) => map + (a -> f(b)) 
    case None => map 
    } 
} 

implicit def toMapUtils[A, B](map: Map[A, B]) = new MapUtils(map) 

val m = Map(1 -> 1) 
m.mapValueAt(1)(_ + 1) 
// Map(1 -> 2) 
m.mapValueAt(2)(_ + 1) 
// Map(1 -> 1) 
3

Oto jeden sposób:

scala> val m = Map(2 -> 3, 5 -> 11) 
m: scala.collection.immutable.Map[Int,Int] = Map(2 -> 3, 5 -> 11) 

scala> m ++ (2, m.get(2).map(1 +)).sequence 
res53: scala.collection.immutable.Map[Int,Int] = Map(2 -> 4, 5 -> 11) 

scala> m ++ (9, m.get(9).map(1 +)).sequence 
res54: scala.collection.immutable.Map[Int,Int] = Map(2 -> 3, 5 -> 11) 

To działa, ponieważ (A, Option[B]).sequence daje Option[(A, B)]. (sequence w ogóle okazuje rodzajów wewnątrz. Tj F[G[A]] => [G[F[A]], zważywszy F : Traverse i G : Applicative.)