Zip несколько последовательностей - PullRequest
32 голосов
/ 09 марта 2012

Я пытаюсь 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))

Однако я хотел бы получить последовательность flat кортежей:

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-х кортежей.

Есть ли способ легко сжать (произвольно) количество последовательностей равной длины?

Ответы [ 7 ]

11 голосов
/ 09 марта 2012

Вот один из способов решения вашего примера, но это не для произвольного числа последовательностей.

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.

6 голосов
/ 09 марта 2012

Я бы создал класс, который представляет наборы данных:

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

Когда исходные списки могут содержать много значений, лучше сделать их равными IndexedSeq, чтобы время доступа было O (1).

5 голосов
/ 16 января 2014

Я думаю, что сопоставление с образцом является хорошим вариантом

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 голосов
/ 09 марта 2012

Используя бесформенный , вы можете сделать:

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. Тогда вам придется вручную создавать списки HL. ]

2 голосов
/ 09 марта 2012

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

Но я хочу добавить другое возможное решение. Если вы хотите сохранить типизацию записей кортежа, но в противном случае заинтересованы в более похожем на коллекцию типе, возможно, HLists (гетерогенные списки) для вас. Вы можете Google hlist scala для реализации и объяснения.

1 голос
/ 15 января 2014

Использование product-collection

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

В настоящее время это работает для арит 1 - 22. Как видите, типы сохраняются.

0 голосов
/ 23 апреля 2019

Вот еще одно решение, которое будет работать для вашей проблемы.

ints zip chars zip strings zip bools map{ case (((a, b), c), d) => (a,b,c,d)}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...