Domanda

Devo spesso trasporre una raccolta di collezioni "rettangolari" a Scala, ad esempio: un elenco di mappe, una mappa di liste, una mappa di mappe, una serie di elenchi, una mappa di set ecc. Dal momento che le raccolte Può essere visualizzato uniformemente come una mappatura da un dominio specifico a un co-dominio (ad esempio: un elenco [A] / array [A] è una mappatura dal dominio INT a un co-dominio, impostato [A] è una mappatura Dal punto di vista del co-dominio booleano ecc.), Vorrei scrivere una funzione pulita e generica per eseguire un'operazione di trasposizione (ad esempio: girare una mappa degli elenchi nell'elenco trasposto delle mappe). Tuttavia, sto avendo problemi perché diverso dall'operatore (), Scala non sembra avere un'API unificata per visualizzare le collezioni Astrattamente come mapping?

Quindi finisco per scrivere una trasposizione separata per ogni tipo di raccolta di raccolta come segue:

def transposeMapOfLists[A,B]( mapOfLists: Map[A,List[B]] ) : List[Map[A,B]] = {
  val k = ( mapOfLists keys ) toList
  val l = ( k map { mapOfLists(_) } ) transpose;
  l map {  v => ( k zip v ) toMap }
}

def transposeListOfMaps[A,B]( listOfMaps: List[Map[A,B]]) : Map[A,List[B]] = {
  val k = ( listOfMaps(0) keys ) toList
  val l = ( listOfMaps map { m => k map { m(_) } } ) transpose;
  ( k zip l ) toMap
}

def transposeMapOfMaps[A,B,C]( mapOfMaps: Map[A,Map[B,C]] ) : Map[B,Map[A,C]] = {
  val k = ( mapOfMaps keys ) toList
  val listOfMaps = k map { mapOfMaps(_) }
  val mapOfLists = transposeListOfMaps( listOfMaps )
  mapOfLists map { p => ( p._1, ( k zip p._2 ) toMap ) }
}
.

Qualcuno può aiutarmi a unificare questi metodi in una raccolta di raccolta generiche trasporre? Mi aiuterà anche (e sono sicuro che gli altri) imparano alcune utili funzionalità Scala nel processo.

PS: Ho ignorato la gestione delle eccezioni e hanno assunto la raccolta di raccolta di input è rettangolare, I.e., tutti gli elementi di dominio delle collezioni interne costituiscono lo stesso set.

È stato utile?

Soluzione

Sono sicuro che la seguente versione disordinata usando le classi di tipo potrebbe essere ripulita molto, ma funziona come una rapida prova di concetto.Non vedo un modo semplice per ottenere i tipi di ritorno a destra senza tipi di metodo dipendenti (sono sicuro che è possibile), quindi dovrai usare -Xexperimental:

trait Mapping[A, B, C] {
  type M[D] <: PartialFunction[A, D]
  def domain(c: C): Seq[A]
  def fromPairs[D](ps: Seq[(A, D)]): M[D]
  def codomain(c: C)(implicit ev: C <:< PartialFunction[A, B]) =
    domain(c).map(c)
  def toPairs(c: C)(implicit ev: C <:< PartialFunction[A, B]) =
    domain(c).map(a => (a, c(a)))
}

implicit def seqMapping[A, B <: Seq[A]] = new Mapping[Int, A, B] {
  type M[C] = Seq[C]
  def domain(c: B) = 0 until c.size
  def fromPairs[C](ps: Seq[(Int, C)]) = ps.sortBy(_._1).map(_._2)
}

implicit def mapMapping[A, B, C <: Map[A, B]] = new Mapping[A, B, C] {
  type M[D] = Map[A, D]
  def domain(c: C) = c.keys.toSeq
  def fromPairs[D](ps: Seq[(A, D)]) = ps.toMap
}

def transpose[A, B, C, M, N](m: M)(implicit
  pev: M <:< PartialFunction[A, N],
  qev: N <:< PartialFunction[B, C],
  mev: Mapping[A, N, M],
  nev: Mapping[B, C, N]
) = nev.fromPairs(nev.domain(mev.codomain(m).head).map(b =>
    b -> mev.fromPairs(mev.toPairs(m).map { case (a, c) => a -> c(b) })
))
.

E ora per alcuni test:

scala> println(transpose(List(Map("a" -> 1, "b" -> 13), Map("b" -> 99, "a" -> 14))))
Map(a -> Vector(1, 14), b -> Vector(13, 99))

scala> println(transpose(Map('a' -> List(1, 2, 3), 'z' -> List(4, 5, 6))))
Vector(Map(a -> 1, z -> 4), Map(a -> 2, z -> 5), Map(a -> 3, z -> 6))

scala> println(transpose(Map("x" -> Map(4 -> 'a, 99 -> 'z), "y" -> Map(4 -> 'b, 99 -> 's))))
Map(4 -> Map(x -> 'a, y -> 'b), 99 -> Map(x -> 'z, y -> 's))
.

Quindi funziona come desiderato.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top