Как добавить unzipWithIndex во все коллекции Scala, где это имеет смысл - PullRequest
1 голос
/ 20 января 2011

Предположим, у меня есть список строк, и я использую zipWithIndex, чтобы преобразовать его в список кортежей:

List("a", "b", "c").zipWithIndex
res1: List[(java.lang.String, Int)] = List((a,0), (b,1), (c,2))

Я хотел бы написать метод unzipWithIndex, который выполняет обратное преобразование, то есть метод, который при применении к списку кортежей, вторые элементы которых являются перестановкой первых N целых чисел, возвращает первые элементы в не переставленном порядке:

List(("c",2), ("a",0), ("b",1)).unzipWithIndex
res2: List[java.lang.String] = List(a, b, c)

Метод должен работать с любой подходящей коллекцией из двух кортежей, вторые элементы которых имеют тип Int, предпочтительно с использованием шаблона Pimp My Library. Как мне поступить с коллекциями Scala 2.8?

Ответы [ 3 ]

16 голосов
/ 20 января 2011
object Test {
  import collection.generic.CanBuildFrom

  class Unzip[T, CC[X] <: Traversable[X]]
    (coll: CC[(T, Int)])
    (implicit bf: CanBuildFrom[CC[(T, Int)], T, CC[T]]) {
    def unzipWithIndex: CC[T] = bf(coll) ++= (coll.toSeq sortBy (_._2) map (_._1)) result
  }

  implicit def installUnzip[T, CC[X] <: Traversable[X]]
    (coll: CC[(T, Int)])
    (implicit bf: CanBuildFrom[CC[(T, Int)], T, CC[T]]) = new Unzip[T, CC](coll)

  def main(args: Array[String]): Unit = {
    val x1 = util.Random.shuffle("abcdefgh".zipWithIndex)

    println("x1 shuffled = " + x1)
    println("x1.unzipWithIndex = " + x1.unzipWithIndex)

    val x2 = (1 to 10).toSet.zipWithIndex

    println("x2 = " + x2)
    println("x2.unzipWithIndex = " + x2.unzipWithIndex)
  }
}

% scala Test
x1 shuffled = Vector((f,5), (g,6), (c,2), (d,3), (e,4), (a,0), (h,7), (b,1))
x1.unzipWithIndex = Vector(a, b, c, d, e, f, g, h)
x2 = Set((8,8), (2,5), (3,7), (5,0), (9,4), (4,9), (6,3), (10,1), (7,6), (1,2))
x2.unzipWithIndex = Set(5, 10, 1, 6, 9, 2, 7, 3, 8, 4)
1 голос
/ 20 января 2011

Вот мой дубль:

implicit def toUnzippable[E](l:List[(E,Int)]) = new AnyRef {
  def unzipWithIndex:List[E] = l.sortBy(_._2).map(_._1)
}

Это делает то, что требует ваш пример. Однако есть две проблемы:

  • Не обрабатывает пропущенные индексы (например, List(("a",0), ("b",2)).unzipWithIndex == List("a","b"))
  • Работает только со списками.

На самом деле вы можете заставить его работать со всеми последовательностями, у которых есть компоновщики, поэтому они возвращают тот же тип последовательности, который вы вставили. Но для этого потребуется дополнительная работа.

1 голос
/ 20 января 2011

Не точное совпадение, но вы могли бы извлечь некоторые идеи из реализации распаковки HList ( кортеж произвольной длины 2 , исходящий из Haskell Сильно типизированный гетерогенныйколлекции ).

См. Программирование на уровне типов в Scala, часть 6d: HList Zip / Unzip из Рунар Эли и Марк Харра .

мы определим класс типа unzip, который принимает HList кортежей и разделяет его на два HList по компонентам:

trait Unzip[H <: HList, R1 <: HList, R2 <: HList] {
   def unzip(h: H): (R1, R2)
}

Разархивирование HNil приводит к HNil.

implicit def unzipNil =
   new Unzip[HNil, HNil, HNil] {
      def unzip(h: HNil) = (HNil, HNil)
   }

Для HCons мы расстегиваем хвост, отделяем компоненты головки и добавляем соответствующий компонент головки к каждому компоненту хвоста.

   implicit def unzipCons[H1, H2, T <: HList, TR1 <: HList, TR2 <: HList]
      (implicit unzipTail: Unzip[T, TR1, TR2]) =

      new Unzip[(H1,H2) :: T, H1 :: TR1, H2 :: TR2]  {
         def unzip(h: (H1,H2) :: T) = {
            val (t1, t2) = unzipTail.unzip(h.tail)
            (HCons(h.head._1, t1), HCons(h.head._2, t2))
         }
      }

   def unzip[H <: HList, R1 <: HList, R2 <: HList](h: H)(implicit un: Unzip[H, R1, R2]): (R1, R2) =
      un unzip h
}

Опять же, нам просто нужно подключить это к нашему классу типов HListOps.

Опираясь на приведенный выше пример,

// unzip the zipped HLists
val (cc1, cc2) = cc.unzip

val (ca, cb) = cc1.unzip
...