9

Мне приходится часто переносить «прямоугольную» коллекцию коллекций в Scala, например: список карт, карту списков, карту карт, набор списков , отображение множеств и т. д. Так как коллекции можно равномерно рассматривать как отображение из определенной области в содомену (например: Список [A]/Array [A] - это отображение из области Int в A-кодовое пространство, domain, Set [A] - это отображение из домена A в булевский co-домен и т. д.), я хотел бы написать чистую общую функцию для выполнения операции транспонирования (например: превратить карту списков в транспонированную список карт). Однако у меня возникают проблемы, потому что кроме оператора() Scala, похоже, не имеет единого API для абстрактного отображения в виде сопоставлений?Транспонирование произвольных коллекций коллекций в Scala

Так что я в конечном итоге написание отдельного транспонирование для каждого типа коллекции-в-коллекции следующим образом:

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

Может кто-то помочь мне объединить эти методы в один общий сбор-из-коллекций переставлять? Это также поможет мне (и я уверен, другие) узнать некоторые полезные функции Scala в этом процессе.

ps: Я проигнорировал обработку исключений и предположил, что входные коллекции коллекций являются прямоугольными, т. Е. Все элементы домена внутренних коллекций составляют один и тот же набор.

ответ

8

Я уверен, что следующая грязная версия, использующая классы типов, может быть очищена очень много, но она работает как быстрая доказательство концепции. Я не вижу простой способ, чтобы получить возвращаемые типы права без зависимых типов методы (я уверен, что это возможно), так что вы должны будете использовать -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) }) 
)) 

И теперь для некоторых тестов:

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

Так оно работает по своему желанию.

+0

Спасибо - это очень полезно! Мне потребовалось некоторое время, чтобы понять, что вы сделали, потому что я не знаком с некоторыми из расширенных функций Scala, которые вы использовали (это отличный повод для меня, чтобы изучить эти функции более подробно сейчас!). – Ashwin

Смежные вопросы