2012-03-09 19 views
30

Próbuję zip wielu sekwencji tworząc długi krotki:pocztowy wielokrotne sekwencje

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 

Co dostaję:

List[(((Int, Char), String), Boolean)] = 
    List((((1,a),Alpha),true), (((2,b),Beta),false), (((3,c),Gamma),false)) 

Jednak chciałbym uzyskać sekwencję płaskich krotek :

List[(Int, Char, String, Boolean)] = 
    List((1,a,Alpha,true), (2,b,Beta,false), (3,c,Gamma,false)) 

Teraz mogę zrobić:

List(ints, chars, strings, bools).transpose 

Ale wraca słabo typowane List[List[Any]]. Również mogę zrobić (ints, chars, strings).zipped, ale zipped działa tylko na 2-tki i 3-tki.

Czy istnieje sposób na łatwe zip (dowolne) liczby sekwencji o równej długości?

Odpowiedz

11

Oto jeden ze sposobów rozwiązania twojego przykładu, ale nie dotyczy to dowolnej liczby sekwencji.

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 

Nie sądzę, że można to zrobić ogólnie dla krotek o dowolnej długości, a przynajmniej nie z tego rodzaju rozwiązaniem. Krotki są silnie typowane, a system typów nie pozwala na określenie zmiennej liczby parametrów, o ile wiem, co uniemożliwia utworzenie uogólnionej wersji f2 i f3, która zajmuje krotkę arbitralnej długości ((A,B),C,D,...) (to zwróci krotkę (A,B,C,D,...)).

Jeśli istnieje sposób na określenie zmiennej liczby parametrów typu, nie potrzebowalibyśmy cech Tuple1, Tuple2, ... Tuple22 w standardowej bibliotece Scali.

+0

+1, dzięki. Obecnie używam podejścia 'map', ale' t._1._1, t._1._2, t._2, t._3' nie jest bardzo czytelny - w moim przypadku potrzebuję 5-krotnej czyni sprawę jeszcze gorszą. Naprawdę nie potrzebuję obsługi dowolnych list długości, ale * wystarczająco długo *. Być może istnieją pewne wyspecjalizowane metody, które zwracają silnie typowane krotki o odpowiedniej długości, ale rozumiem, że chodzi o problem 'Tuple1' -'Tuple22'. –

+6

Dzięki dopasowaniu do wzorca można pozbyć się nieczytelnej składni "._1, ._2" itd .: 'def f2 [A, B, C] (t: ((A, B), C)) = t dopasowanie {case ((a, b), c) => (a, b, c)} ' – Jesper

2

Podzielam opinię Jespera, że ​​nie jest to ogólnie możliwe, ponieważ każda krotka jest reprezentowana jako oddzielna klasa w kodzie źródłowym, więc musisz napisać osobny kod, aby uzyskać do nich dostęp, chyba że używasz generatora kodu.

Ale chcę dodać inne możliwe rozwiązanie. Jeśli chcesz zachować typowanie swoich wpisów krotek, ale w inny sposób są zainteresowane typem bardziej przypominającym kolekcję, być może HLists (listy heterogeniczne) są dla ciebie. Możesz wprowadzić google hlist scala dla implementacji i objaśnień.

5

Korzystanie shapeless, można zrobić:

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

(nie jestem pewien, czy istnieje więcej implicts zdefiniowany, który pozwala uniknąć map i tyłu i do przodu konwersje)

[Edit : Ta część nie jest już prawdą.

Pamiętaj, że bezkształtne dokumenty mówią, że obecnie obsługiwane są tylko konwersje do Tuple4. W takim razie musisz ręcznie utworzyć listę HLists.]

+0

W rzeczywistości bezkształtny obsługuje wszystkie arniki do 22. –

+0

Ah, ok. Przechodziłem komentarzem w pliku conversionions.scala, nie sprawdzając go. – Debilski

+0

Ups ... nieaktualny komentarz. Naprawiono teraz. Dzięki za heads up. –

6

Chciałbym utworzyć klasę, która reprezentuje zbiory danych:

case class DataSet(int: Int, char: Char, string: String, bool: Boolean) 

To przynosi ładniejsze nazwy dostępu do wartości zamiast _N mamy w krotek. Jeżeli wykazy mogą mieć różne rozmiary najkrótsza powinna być wybrana:

val min = List(ints, chars, strings, bools).map(_.size).min 

Teraz jest to możliwe, aby wyodrębnić dane:

val dataSets = (0 until min) map { i => DataSet(ints(i), chars(i), strings(i), bools(i)) } 

Kiedy oryginalne listy mogą zawierać wiele wartości, lepiej jest zmień je na IndexedSeq, aby czas dostępu wynosił 0 (1).

1

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

ta obecnie pracuje na liczbę operandów 1 - 22. Jak widać typy są zachowane.

3

myślę, pasujące do wzorca jest dobrym rozwiązaniem

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

lub można dodać rodzaj oraz

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