Могу ли я закрепить более двух списков в 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:. Как оказалось, для моего конкретного случая использования лучше иметь список списков, а не список кортежей, поэтому я принимаю реакцию тыквы. Это также самый простой, поскольку он использует собственный метод.

Ответ 1

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

Ответ 2

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

Для справок в будущем.

Ответ 3

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

Чтобы закрепить 3 коллекции:

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

Ответ 4

Да, с zip3.

Ответ 5

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

Ответ 6

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

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

Ответ 7

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

Например, если ваш ввод был 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))
}

Ответ 8

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

import scala.{Tuple2 => &}

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

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

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

Ответ 9

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

Ответ 10

С 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"))((_, _, _, _, _, _, _, _, _, _, _, _))