Pergunta

Eu tenho que, muitas vezes, transpor um "retangular" coleção de coleções, em Scala, por exemplo:uma lista de mapas, um mapa da lista, um mapa de mapas, um conjunto de listas, um mapa de conjuntos, etc.Desde as coleções podem ser uniformemente visto como um mapeamento de um domínio específico para o co-domínio (e.g.:uma Lista[a]/Matriz[A] é um mapeamento do Int de domínio para o co-domínio, o Conjunto[A]é um mapeamento de Um domínio para o Booleano co-domínio, etc.), Eu gostaria de escrever um limpo, genérico função para fazer uma transposição de operação (por exemplo:virar um mapa de listas para a transposição lista de mapas).No entanto, estou tendo problemas porque os outros que o operador (), Scala, não parece ter uma API unificada para visualizar coleções abstratamente como mapeamentos ?

Assim que eu acabar de escrever um separado transpor para cada tipo de coleção-de-coleções da seguinte forma:

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 ) }
}

Alguém pode me ajudar a unificar esses métodos em um conjunto genérico de coleções de transpor ?Ele também irá me ajudar (e tenho certeza de que outros) aprender algumas útil Scala recursos no processo.

ps:Eu ter ignorado a manipulação de exceção e de ter assumido a entrada coleção de coleções é retangular, por exemplo, todos do interior de coleções de domínio dos elementos que constituem o mesmo conjunto.

Foi útil?

Solução

Tenho certeza de que o seguinte confuso versão usando o tipo de classes pode ser limpo muito, mas funciona como uma rápida prova-de-conceito.Eu não vejo uma maneira fácil de obter os tipos de retorno, sem dependentes tipos de método (tenho certeza de que é possível), então você vai ter que usar -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 agora, para alguns testes:

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))

Então a funcionar como desejado.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top