2012-04-20 9 views
14

mam niezmienną strukturę danych gdzie mam zagnieżdżony wartości w mapach, tak:Unikanie powtarzania za pomocą soczewek podczas głębokiego kopiowania na Mapie wartości

case class TradingDay(syms: Map[String, SymDay] = Map.empty) 
case class SymDay(sym: String, traders: Map[String, TraderSymDay] = Map.empty) 
case class TraderSymDay(trader: String, sym: String, trades: List[Trade] = Nil) 

osobno Mam listę wszystkich transakcji ponad dzień, i chcę, aby wygenerować strukturę TradingDay, gdzie

case class Trade(sym: String, trader: String, qty: Int) 

próbuję dowiedzieć się, w jaki sposób mogę zaktualizować tę strukturę z soczewek (patrz załącznik) poprzez składanie przez moich transakcji:

(TradingDay() /: trades) { (trd, d) => 
    def sym = trd.sym 
    def trader = trd.trader 
    import TradingDay._ 
    import SymDay._ 
    import TraderSymDay._ 
    val mod = 
    for { 
     _ <- (Syms member sym).mods(
      _ orElse some(SymDay(sym))) 
     _ <- (Syms at sym andThen Traders member trader).mods(
      _ orElse some(TraderSymDay(trader, sym))) 
     _ <- (Syms at sym andThen (Traders at trader) andThen Trades).mods(
      trd :: _) 
     x <- init 
    } yield x 
    mod ! d 
} 

To działa; ale zastanawiam się, czy mógłbym być mniej powtarzalny (jeśli chodzi o dodawanie do mapy, a następnie modyfikowanie wartości w kluczu mapy, nie wydaje się to o wiele mniej irytujące niż powiązana z nią głęboka kopia.)

Dodatek - soczewki

object TradingDay { 
    val Syms = Lens[TradingDay, Map[String, SymDay]](_.syms, (d, s) => d.copy(syms = s)) 
} 

object SymDay { 
    val Traders = Lens[SymDay, Map[String, TraderSymDay]](_.traders, (d, t) => d.copy(traders = t)) 
} 

object TraderSymDay { 
    val Trades = Lens[TraderSymDay, List[Trade]](_.trades, (d, f) => d.copy(trades = f)) 
} 
+3

+1 na pytanie z dodatku – ziggystar

Odpowiedz

0

odpowiedzi udzielonej przez Jordan Zachodzie (@_jrwest)

To tylko niewielka zmiana i obejmuje wprowadzenie następującej konwersji:

implicit def myMapLens[S,K,V] = MyMapLens[S,K,V](_) 
case class MyMapLens[S,K,V](lens: Lens[S,Map[K,V]]) { 
    def putIfAbsent(k: K, v: => V) 
    = lens.mods(m => m get k map (_ => m) getOrElse (m + (k -> v))) 
} 

Wtedy możemy korzystać z tego, co następuje:

(TradingDay() /: trades) { (d, trade) => 
    def sym = trade.sym 
    def trader = trade.trader 
    def traders = Syms at sym andThen Traders 
    def trades = Syms at sym andThen (Traders at trader) andThen Trades 
    val upd = 
    for { 
     _ <- Syms putIfAbsent (sym, SymDay(sym)) 
     _ <- traders putIfAbsent (trader, TraderSymDay(trader, sym)) 
     _ <- trades.mods(trade :: _) 
    } yield() 
    upd ~> d 
} 
+1

Istnieje już taka niejawna konwersja w kodzie źródłowym Scalaz. Brakuje tylko parametru putIfAbsent. Dbaj o prośbę o odciągnięcie? –

6

z

type @>[A,B] = Lens[A, B] 

i utrzymywanie tego obiektywu

val Syms : Lens[TradingDay, Map[String, SymDay]] 

i definiujące te obiektywy:

val F : Map[String, SymDay] @> Option[SymDay] = ... 
val G : Option[SymDay] @> Map[String, TraderSymDay] = ... 
val H : Map[String, TraderSymDay] @> Option[TraderSymDay] = ... 
val I : Option[TraderSymDay] @> List[Trade] = ... 

val J: TradingDay @> List[Trade] = Syms >=> F >=> G >=> H >=> I 

można uzyskać to:

(trades /: TradingDay()){ (trd, d) => (J.map(trd :: _).flatMap(_ => init)) ! d } 
+0

Jestem mylić o tym, jak to może działać; gdzie są dodawane wewnętrzne mapy? Na przykład; oczywista implementacja 'G' zwróci pustą mapę dla' None'. Czy jest jakaś szansa, żeby to uzupełnić, żeby udowodnić, że to działa? –

+0

Pewnie. BTW, wydaje się, że polegasz bardziej na monadzie państwowej niż Lens. Obiektywy są bardzo dobre podczas uzyskiwania dostępu do zagnieżdżonych właściwości algebraicznych typów danych. Dostępy te nie zależą od stanu zewnętrznego. –