Zip несколько последовательностей
Я пытаюсь zip
несколько последовательностей формировать длинный кортеж:
val ints = List(1,2,3)
val chars = List('a', 'b', 'c')
val strings = List("Alpha", "Beta", "Gamma")
val bools = List(true, false, false)
ints zip chars zip strings zip bools
Что я получаю:
List[(((Int, Char), String), Boolean)] =
List((((1,a),Alpha),true), (((2,b),Beta),false), (((3,c),Gamma),false))
Однако я хотел бы получить последовательность плоских кортежей:
List[(Int, Char, String, Boolean)] =
List((1,a,Alpha,true), (2,b,Beta,false), (3,c,Gamma,false))
Теперь я могу сделать:
List(ints, chars, strings, bools).transpose
Но он возвращает слабо типизированный List[List[Any]]
. Также я могу сделать (ints, chars, strings).zipped
, но zipped
работает только с 2-мя корнями и 3-мя кортежами.
Есть ли способ легко zip (произвольное) число равномерной последовательности?
Ответы
Ответ 1
Здесь один из способов решения вашего примера, но это не для произвольного количества последовательностей.
val ints = List(1,2,3)
val chars = List('a', 'b', 'c')
val strings = List("Alpha", "Beta", "Gamma")
val bools = List(true, false, false)
val input = ints zip chars zip strings zip bools
// Flattens a tuple ((A,B),C) into (A,B,C)
def f2[A,B,C](t: ((A,B),C)) = (t._1._1, t._1._2, t._2)
// Flattens a tuple ((A,B),C,D) into (A,B,C,D)
def f3[A,B,C,D](t: ((A,B),C,D)) = (t._1._1, t._1._2, t._2, t._3)
input map f2 map f3
Я не думаю, что это возможно сделать в общем случае для кортежей произвольной длины, по крайней мере, не с таким решением. Кортежи строго типизированы, и система типов не позволяет вам указать переменное количество параметров типа, насколько я знаю, что делает невозможным создание обобщенной версии f2
и f3
, которая принимает кортеж произвольной длины ((A,B),C,D,...)
(который вернет кортеж (A,B,C,D,...)
).
Если бы можно было указать переменное число параметров типа, нам не нужны черты Tuple1
, Tuple2
,... Tuple22
в стандартной библиотеке Scala.
Ответ 2
Используя shapeless, вы можете сделать:
import shapeless.Tuples._
val ints = (1, 2, 3)
val chars = ('a', 'b', 'c')
val megatuple = (ints, chars)
val megahlist = (megatuple hlisted) map hlisted
val transposed = (mhlist transpose) map tupled tupled
scala> transposed
res: ((Int, Char), (Int, Char), (Int, Char)) = ((1,a),(2,b),(3,c))
(не уверен, если определено больше имплиций, позволяющих избежать преобразований map
и обратно-вперед)
[ Изменить: эта часть больше не верна.
Обратите внимание, что бесформенные документы говорят, что в настоящее время поддерживаются только преобразования до Tuple4
. Вы должны вручную создать HLists тогда.]
Ответ 3
Я бы создал класс, который представляет наборы данных:
case class DataSet(int: Int, char: Char, string: String, bool: Boolean)
Это дает более приятные имена для доступа к значениям вместо _N
, которые мы имеем в кортежах. Если списки могут иметь разные размеры, следует выбрать самый короткий:
val min = List(ints, chars, strings, bools).map(_.size).min
Теперь можно извлечь данные:
val dataSets = (0 until min) map { i => DataSet(ints(i), chars(i), strings(i), bools(i)) }
Когда исходные списки могут содержать множество значений, лучше сделать их , чтобы время доступа было O (1).
Ответ 4
Я думаю, что сопоставление образцов является хорошим вариантом
val ints = List(1,2,3)
val chars = List('a', 'b', 'c')
val strings = List("Alpha", "Beta", "Gamma")
val bools = List(true, false, false)
(ints zip chars zip strings zip bools) map { case (((i,c),s),b) => (i,c,s,b)}
**res1: List[(Int, Char, java.lang.String, Boolean)] = List((1,a,Alpha,true), (2,b,Beta,false), (3,c,Gamma,false))**
или вы также можете добавить тип
(ints zip chars zip strings zip bools) map {case (((i:Int,c:Char),s:String),b:Boolean) => (i,c,s,b)}
**res2: List[(Int, Char, java.lang.String, Boolean)] = List((1,a,Alpha,true), (2,b,Beta,false), (3,c,Gamma,false))**
Ответ 5
Я разделяю мнение Jesper о том, что это невозможно вообще, так как каждый атрибут кортежа представлен как отдельный класс в исходном коде, поэтому вам нужно написать отдельный код для доступа к ним, если не использовать генератор кода.
Но я хочу добавить еще одно возможное решение. Если вы хотите сохранить ввод ваших записей в кортеж, но иначе интересуетесь более похожим на коллекцию типом, возможно, HLists (гетерогенные списки) для вас. Вы можете google hlist scala
для реализаций и объяснений.
Ответ 6
Использование product-collections
scala> ints flatZip chars flatZip strings flatZip bools
res0: org.catch22.collections.immutable.CollSeq4[Int,Char,String,Boolean] =
CollSeq((1,a,Alpha,true),
(2,b,Beta,false),
(3,c,Gamma,false))
В настоящее время это работает для arity 1 - 22. Как вы можете видеть, типы сохраняются.