2012-01-25 14 views

Respuesta

31

No hay adjust en la API Map, desafortunadamente. A veces he utilizado una función como la siguiente (el modelo de Haskell de Data.Map.adjust, con un orden diferente de argumentos):

def adjust[A, B](m: Map[A, B], k: A)(f: B => B) = m.updated(k, f(m(k))) 

Ahora adjust(m, "Mark")(_ - 50) hace lo que quiere. También puede usar el pimp-my-library pattern para obtener la sintaxis más natural de m.adjust("Mark")(_ - 50), si realmente desea algo más limpio.

(Tenga en cuenta que la versión corta por encima de una excepción si k no está en el mapa, que es diferente del comportamiento Haskell y probablemente algo que uno desea fijar en el código real.)

+3

Esto solo funciona si la clave existe en el mapa. Considere 'map.get (k) .fold (map) (b => map.actualizado (k, f (b))) 'si desea ignorar una clave faltante, o un enfoque que tiene' f: opción [B] => B' si desea poder establecer la clave en su ausencia. – adamnfish

+0

@adamnfish: adjust (m, "Mark") (_. GetOrElse (0) - 50) – mdenton8

+0

Muy útil - ¡Ojalá 'adjust' estuviera en la biblioteca estándar! (Probablemente la variante de pliegues de @ adamnfish ..) –

12

Esto podría hacerse con lentes. La idea misma de una lente es poder acercar una parte particular de una estructura inmutable, y ser capaz de: 1) recuperar la parte más pequeña de una estructura más grande, o 2) crear una nueva estructura más grande con una parte más pequeña modificada . En este caso, lo que deseas es # 2.

En primer lugar, un simple implementación de Lens, robado de this answer, robado de scalaz:

case class Lens[A,B](get: A => B, set: (A,B) => A) extends Function1[A,B] with Immutable { 
    def apply(whole: A): B = get(whole) 
    def updated(whole: A, part: B): A = set(whole, part) // like on immutable maps 
    def mod(a: A)(f: B => B) = set(a, f(this(a))) 
    def compose[C](that: Lens[C,A]) = Lens[C,B](
    c => this(that(c)), 
    (c, b) => that.mod(c)(set(_, b)) 
) 
    def andThen[C](that: Lens[B,C]) = that compose this 
} 

A continuación, un constructor inteligente para crear una lente de "estructura mayor" Map[A,B] a "pequeña parte" Option[B]. Indicamos qué "parte más pequeña" queremos ver proporcionando una clave en particular. (Inspirado por lo que recuerdo de Edward Kmett's presentation on Lenses in Scala):

def containsKey[A,B](k: A) = Lens[Map[A,B], Option[B]](
    get = (m:Map[A,B]) => m.get(k), 
    set = (m:Map[A,B], opt: Option[B]) => opt match { 
    case None => m - k 
    case Some(v) => m + (k -> v) 
    } 
) 

Ahora su código puede escribirse:

val m2 = containsKey("Mark").mod(m)(_.map(_ - 50)) 

n.b. De hecho, cambié mod de la respuesta que le robé para que tome sus entradas al curry. Esto ayuda a evitar anotaciones de tipo extra. También note _.map, porque recuerde, nuestra lente es de Map[A,B] a Option[B]. Esto significa que el mapa no se modificará si no contiene la clave "Mark". De lo contrario, esta solución termina siendo muy similar a la solución adjust presentada por Travis.

7

Un SO Answer propone otra alternativa, usando el operador de |+| scalaz

val m2 = m |+| Map("Mark" -> -50) 

El operador |+| se resumen los valores de una clave existente, o insertar el valor bajo un duplicado de la llave.

Cuestiones relacionadas