Могу ли я застегнуть более двух списков вместе в Scala?


учитывая следующий список Scala:

val l = List(List("a1", "b1", "c1"), List("a2", "b2", "c2"), List("a3", "b3", "c3"))

как я могу сделать:

List(("a1", "a2", "a3"), ("b1", "b2", "b3"), ("c1", "c2", "c3"))

поскольку zip можно использовать только для объединения двух списков, я думаю, вам нужно будет как-то перебирать/уменьшать основной список. Неудивительно, что следующее не работает:

scala> l reduceLeft ((a, b) => a zip b)
<console>:6: error: type mismatch;
 found   : List[(String, String)]
 required: List[String]
       l reduceLeft ((a, b) => a zip b)

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

обновление: Я ищу решение, которое может принимать список из N списков с M элементы каждый и создать список M TupleNs.

обновление 2: как оказалось, для моего конкретного случая использования лучше иметь список списков, а не список кортежей, поэтому я принимаю ответ тыквы. Это тоже простой, так как он использует собственный метод.

10 76

10 ответов:

Я не верю, что можно создать список кортежей произвольного размера, но транспонирования функция делает именно то, что вам нужно, если вы не возражаете получать, а не список списков.

scala> (List(1,2,3),List(4,5,6),List(7,8,9)).zipped.toList
res0: List[(Int, Int, Int)] = List((1,4,7), (2,5,8), (3,6,9))

для дальнейшего использования.

таким образом, этот фрагмент кода не будет отвечать потребностям OP, и не только потому, что это четырехлетний поток, но он отвечает на вопрос заголовка, и, возможно, кто-то даже может найти его полезным.

для zip 3 коллекции:

as zip bs zip cs map { 
  case ((a,b), c) => (a,b,c)
}

да, с zip3.

Scala рассматривает все свои различные размеры кортежей как разные классы (Tuple1,Tuple2,Tuple3,Tuple4,...,Tuple22) в то время как они все наследуют от Product признак, этот признак не несет достаточно информации, чтобы фактически использовать значения данных из разных размеров кортежей, если все они могут быть возвращены одной и той же функцией. (И дженерики scala недостаточно сильны, чтобы справиться с этим делом.)

лучше всего написать перегрузки функции zip для все 22 размера кортежей. Генератор кода, вероятно, поможет вам в этом.

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

например, если ваш вход был List(List(1,2), List (3,4)), то тип возврата будет List[Tuple2[Int]]. Если бы он имел три элемента, возвращаемый тип был бы List[Tuple3[Int]], и так далее.

вы можете вернуть List [AnyRef] или даже List[Product], а затем сделать кучу случаев, по одному для каждого состояние.

что касается транспозиции общего списка, это работает:

def transpose[T](l: List[List[T]]): List[List[T]] = l match {
  case Nil => Nil
  case Nil :: _ => Nil
  case _ => (l map (_.head)) :: transpose(l map (_.tail))
}

transpose делает трюк. Возможный алгоритм:

def combineLists[A](ss:List[A]*) = {
    val sa = ss.reverse;
    (sa.head.map(List(_)) /: sa.tail)(_.zip(_).map(p=>p._2 :: p._1))
}

например:

combineLists(List(1, 2, 3), List(10,20), List(100, 200, 300))
// => List[List[Int]] = List(List(1, 10, 100), List(2, 20, 200))

ответ усекается до размера самого короткого списка на входе.

combineLists(List(1, 2, 3), List(10,20))
// => List[List[Int]] = List(List(1, 10), List(2, 20))

если вы не хотите спускаться по прикладному маршруту scalaz / cats/(вставьте свой любимый функциональный lib здесь), сопоставление шаблонов-это путь, хотя (_, _) синтаксис немного неудобен с вложенностью, поэтому давайте изменим его:

import scala.{Tuple2 => &}

for (i1 & i2 & i3 & i4 <- list1 zip list2 zip list3 zip list4) yield (i1, i2, i3, i4)

The & это произвольный выбор здесь, все, что выглядит красиво инфикс должен сделать это. Однако вы, вероятно, получите несколько поднятых бровей во время просмотра кода.

Он также должен работать со всем, что вы можете zip (например Future s)

product-collections естьflatZip работа до arity 22.

scala> List(1,2,3) flatZip Seq("a","b","c") flatZip Vector(1.0,2.0,3.0) flatZip Seq(9,8,7)
res1: com.github.marklister.collections.immutable.CollSeq4[Int,String,Double,Int] = 
CollSeq((1,a,1.0,9),
        (2,b,2.0,8),
        (3,c,3.0,7))

С Scalaz:

import scalaz.Zip
import scalaz.std.list._

// Zip 3
Zip[List].ap.tuple3(List("a1", "b1"),
                    List("a2", "b2"),
                    List("a3", "b3"))

// Zip 4
Zip[List].ap.tuple4(List("a1", "b1"),
                    List("a2", "b2"),
                    List("a3", "b3"),
                    List("a4", "b4"))

// Zip 5
Zip[List].ap.tuple5(List("a1", "b1"),
                    List("a2", "b2"),
                    List("a3", "b3"),
                    List("a4", "b4"),
                    List("a5", "b5"))

для более чем 5:

// Zip 6
Zip[List].ap.apply6(List("a1", "b1"),
                    List("a2", "b2"),
                    List("a3", "b3"),
                    List("a4", "b4"),
                    List("a5", "b5"),
                    List("a6", "b6"))((_, _, _, _, _, _))

// Zip 7
Zip[List].ap.apply7(List("a1", "b1"),
                    List("a2", "b2"),
                    List("a3", "b3"),
                    List("a4", "b4"),
                    List("a5", "b5"),
                    List("a6", "b6"),
                    List("a7", "b7"))((_, _, _, _, _, _, _))

...

// Zip 12
Zip[List].ap.apply12(List("a1", "b1"),
                     List("a2", "b2"),
                     List("a3", "b3"),
                     List("a4", "b4"),
                     List("a5", "b5"),
                     List("a6", "b6"),
                     List("a7", "b7"),
                     List("a8", "b8"),
                     List("a9", "b9"),
                     List("a10", "b10"),
                     List("a11", "b11"),
                     List("a12", "b12"))((_, _, _, _, _, _, _, _, _, _, _, _))