2012-01-25 9 views

risposta

31

Non ci sono adjust nell'API Map, sfortunatamente. Qualche volta ho usato una funzione come la seguente (sul modello di Haskell Data.Map.adjust, con un diverso ordine di argomenti):

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

Ora adjust(m, "Mark")(_ - 50) fa ciò che si vuole. Puoi anche utilizzare lo pimp-my-library pattern per ottenere la sintassi più naturale di m.adjust("Mark")(_ - 50), se vuoi davvero qualcosa di più pulito.

(Si noti che la versione corta sopra genera un'eccezione se k non è nella mappa, che è diverso dal comportamento Haskell e, probabilmente, qualcosa che ci si vuole risolvere nel codice vero e proprio.)

+3

Questo funziona solo se la chiave esiste nella mappa. Prendi in considerazione 'map.get (k) .fold (map) (b => map.aggiornato (k, f (b))) 'se vuoi ignorare una chiave mancante, o un approccio che ha' f: Option [B] => B' se vuoi essere in grado di impostare la chiave in sua assenza. – adamnfish

+0

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

+0

Molto utile - Vorrei che 'adjust' fosse nella libreria standard! (probabilmente la variante fold di @adamnfish ..) –

12

Questo potrebbe da fare con gli obiettivi . L'idea stessa di un obiettivo è di essere in grado di ingrandire una particolare parte di una struttura immutabile e di essere in grado di 1) recuperare la parte più piccola da una struttura più grande, oppure 2) creare una nuova struttura più grande con una parte più piccola modificata . In questo caso, ciò che desideri è # 2.

In primo luogo, una semplice implementazione di Lens, rubato this answer, rubato 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 
} 

successivo, un costruttore intelligente per creare una lente da "struttura più ampia" Map[A,B] a "parte minore" Option[B]. Indichiamo quale "parte più piccola" vogliamo vedere fornendo una chiave particolare. (Ispirato da quello che mi ricordo da 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) 
    } 
) 

Ora il codice si può scrivere:

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

N.B. In realtà ho cambiato mod dalla risposta che ho rubato in modo che tenga i suoi ingressi al curry. Questo aiuta ad evitare annotazioni di tipo extra. Nota anche _.map, perché ricorda che il nostro obiettivo è da Map[A,B] a Option[B]. Ciò significa che la mappa rimarrà invariata se non contiene la chiave "Mark". Altrimenti, questa soluzione finisce per essere molto simile alla soluzione adjust presentata da Travis.

7

Un SO Answer propone un'altra alternativa, utilizzando l'operatore |+| da scalaz

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

L'operatore |+| sarà sommare i valori di una chiave esistente, oppure inserire il valore sotto una nuova chiave.

Problemi correlati