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.
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
@adamnfish: adjust (m, "Mark") (_. GetOrElse (0) - 50) – mdenton8
Muy útil - ¡Ojalá 'adjust' estuviera en la biblioteca estándar! (Probablemente la variante de pliegues de @ adamnfish ..) –