Czy w Scali mogę spakować więcej niż dwie listy?

94

Biorąc pod uwagę następującą listę Scala:

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

Jak mogę zdobyć:

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

Ponieważ zip może być używany tylko do łączenia dwóch list, myślę, że musiałbyś jakoś powtórzyć / zmniejszyć główną listę. Nic dziwnego, że poniższe rozwiązania nie działają:

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)

Jakieś sugestie, jak to zrobić? Myślę, że brakuje mi bardzo prostego sposobu, aby to zrobić.

Aktualizacja: Szukam rozwiązania, które może wziąć Listę N List z M elementami każdy i utworzyć Listę M TupleNs.

Aktualizacja 2: Jak się okazuje, dla mojego konkretnego przypadku lepiej jest mieć listę list, a nie listę krotek, więc akceptuję odpowiedź dyni. Jest też najprostszy, ponieważ wykorzystuje metodę natywną.

pr1001
źródło
możliwy duplikat wielu sekwencji Zip
Suma
Zdecydowanie warte odnotowania: stackoverflow.com/questions/1683312/…
Venkat Sudheer Reddy Aedama
@VenkatSudheerReddyAedama Również zadane przeze mnie pięć dni później. ;-)
pr1001 22.07.15

Odpowiedzi:

36

Nie sądzę, aby można było wygenerować listę krotek o dowolnym rozmiarze, ale funkcja transpozycji robi dokładnie to, czego potrzebujesz, jeśli nie masz nic przeciwko otrzymywaniu listy list.

copumpkin
źródło
Dzięki, to działa idealnie! Kiedy przechodzę do mojego konkretnego przypadku użycia, widzę, że lista list i tak byłaby lepsza, ponieważ muszę zmapować i zredukować różne listy podrzędne.
pr1001
2
@JoshCason w najwęższym znaczeniu „więcej niż dwa”, oczywiście. Trzy to rzeczywiście więcej niż dwa. Zinterpretowałem to pytanie w szerszym znaczeniu „więcej niż dwóch”, czyli arbitralnie wielu. W takim przypadku nie można zrobić tego, czego chce pytanie, chyba że sięgniesz po HLists i tym podobne.
copumpkin
link w odpowiedzi jest uszkodzony, nowy link to scala-lang.org/api/2.12.1/scala/…
Ramesh Maharjan
216
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))

Dla przyszłego odniesienia.

Xorlev
źródło
32
To jest świetne do spakowania trzech list. Szkoda, że ​​to nie działa na więcej niż trzech listach :(
theon
2
Zwróć uwagę, że najpierw musi to być krotka: zippednie jest funkcją List.
Nathaniel Ford
6
zippedjest przestarzałe w Scali 2.13. w 2.13, dol1.lazyZip(l2).lazyZip(l3).toList
Seth Tisue
30

Więc ten fragment kodu nie będzie odpowiadał na potrzeby OP, i to nie tylko dlatego, że jest to wątek czteroletni, ale odpowiada na tytułowe pytanie i być może ktoś może nawet uznać go za przydatny.

Aby spakować 3 kolekcje:

as zip bs zip cs map { 
  case ((a,b), c) => (a,b,c)
}
Bijou Trouvaille
źródło
do zrobienia 4 kolekcji wygląda następująco:as zip bs zip cs zip ds map { case ((a,b),c)} map {case ((a,b),c,d)=>(a,b,c,d)}
James Tobin
1
@JamesTobin, u as zip bs zip cs zip ds map {case (((a,b),c),d)=>(a,b,c,d) }
shorten to
Przyjemne dla list różnego typu.
FP Freely
11

Tak, z zip3 .

Harold L.
źródło
2
Dzięki, ale działa tylko z 3 listami. Szukam rozwiązania, które może wziąć Listę N List z M elementami każdy i utworzyć Listę M TupleNs.
pr1001
6

transposeZrób sztuczkę. Możliwym algorytmem jest:

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

Na przykład:

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

Odpowiedź jest obcinana do rozmiaru najkrótszej listy w danych wejściowych.

combineLists(List(1, 2, 3), List(10,20))
// => List[List[Int]] = List(List(1, 10), List(2, 20))
WP McNeill
źródło
1
ta odpowiedź prawie załatwia sprawę, jednak odwraca elementy. Czy możesz zaproponować ulepszoną wersję, która generuje dane wyjściowe w oczekiwanej kolejności? dzięki
fracca
Zmodyfikowana wersja, która zachowuje kolejność: def combineLists[A](ss:List[A]*) = { val sa = ss.reverse; (sa.head.map(List(_)) /: sa.tail)(_.zip(_).map(p=>p._2 :: p._1)) }
rogermenezes
5

Scala traktuje wszystkich swoich różnych rozmiarach krotki jak różnych klas ( Tuple1, Tuple2, Tuple3, Tuple4, ..., Tuple22), podczas gdy robią wszystko dziedziczą z Productcechą, że cecha nie prowadzi wystarczająco dużo informacji, aby rzeczywiście skorzystać z wartości danych z różnych rozmiarów krotek gdyby wszystkie mogły zostać zwrócone przez tę samą funkcję. (A typy generyczne Scala również nie są wystarczająco wydajne, aby obsłużyć ten przypadek).

Najlepszym rozwiązaniem jest zapisanie przeciążeń funkcji zip dla wszystkich 22 rozmiarów krotki. Prawdopodobnie pomoże Ci w tym generator kodu.

Ken Bloom
źródło
5

Jeśli nie chcesz przechodzić przez ścieżkę aplikacyjną scalaz / cats / (wstaw tutaj swoją ulubioną bibliotekę funkcjonalną), najlepszym rozwiązaniem jest dopasowanie wzorców, chociaż (_, _)składnia jest nieco niezręczna przy zagnieżdżaniu, więc zmieńmy to:

import scala.{Tuple2 => &}

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

Wybór &jest tutaj arbitralny, wszystko, co wygląda ładnie wrostek, powinno to zrobić. Jednak prawdopodobnie podczas przeglądu kodu pojawi się kilka uniesionych brwi.

Powinien również działać ze wszystkim, co możesz zip(np. Future)

L4Z
źródło
5

Nie wierzę, że jest to możliwe bez powtarzania się. Z jednego prostego powodu: nie możesz zdefiniować zwracanego typu funkcji, o którą prosisz.

Na przykład, jeśli dane wejściowe to List(List(1,2), List(3,4)), to zwracany byłby typ List[Tuple2[Int]]. Gdyby miał trzy elementy, zwracany byłby typ List[Tuple3[Int]]i tak dalej.

Możesz zwrócić List[AnyRef], a nawet List[Product]zrobić kilka przypadków, po jednym dla każdego warunku.

Jeśli chodzi o ogólną transpozycję listy, działa to:

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))
}
Daniel C. Sobral
źródło
To nie zadziała w przypadku list o dowolnej wielkości. Na przykład: transpose (List (List ("a", "b"), List ("c")))
Venkat Sudheer Reddy Aedama
1
@VenkatSudheerReddyAedama Transpozycja niekompletnych macierzy nie ma dla mnie sensu. Aby wziąć twój przykład, jeśli czgadzasz się z aczy z b? Jak byś to przedstawił, będąc w zgodzie z innymi?
Daniel C. Sobral
Zgoda. To niekompletna macierz. Szukałem czegoś na wzór zipAll. Powiedzmy, że w moim przypadku cjest zgodne z a(tj. Zgodne z indeksem)?
Venkat Sudheer Reddy Aedama
2

kolekcje produktów mają flatZipoperację do 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))
Mark Lister
źródło
0

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

Powyżej 5 lat:

// 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"))((_, _, _, _, _, _, _, _, _, _, _, _))
ZhekaKozlov
źródło