Nie jest to możliwe (z wyjątkiem „ręcznego”) AFAIK. Biorąc pod uwagę def toTuple(x: List[Int]): R, jaki powinien być typ R?
7
Jeśli nie trzeba tego robić dla krotki o dowolnym rozmiarze (np. Jak w przypadku hipotetycznej metody przedstawionej powyżej), rozważ x match { case a :: b :: c :: Nil => (a, b, c); case _ => (0, 0, 0) }i zwróć uwagę, że wynikowy typ jest ustalony naTuple3[Int,Int,Int]
2
Chociaż to, co chcesz zrobić, nie jest możliwe List, możesz przyjrzeć się typowi bezkształtnemu HList, który umożliwia konwersję do iz krotek ( github.com/milessabin/ ... ). Może ma to zastosowanie do twojego przypadku użycia.
Odpowiedzi:
59
Nie możesz tego zrobić w bezpieczny sposób. Czemu? Ponieważ ogólnie nie możemy znać długości listy do czasu wykonania. Ale „długość” krotki musi być zakodowana w jej typie, a zatem znana w czasie kompilacji. Na przykład (1,'a',true)ma typ (Int, Char, Boolean), dla którego jest cukier Tuple3[Int, Char, Boolean]. Powodem, dla którego krotki mają to ograniczenie, jest to, że muszą być w stanie obsłużyć niejednorodne typy.
Czy istnieje lista elementów tego samego typu o stałym rozmiarze? Oczywiście nie importowanie niestandardowych bibliotek, takich jak bezkształtne.
1
@davips nie jest to, o czym wiem, ale łatwo jest zrobić własne, np.case class Vec3[A](_1: A, _2: A, _3: A)
Tom Crockett
To byłaby „krotka” elementów tego samego typu, na przykład nie mogę zastosować do niej mapy.
1
@davips, tak jak w przypadku każdego nowego typu danych, musisz zdefiniować, jak mapdziała w nim itp.
Tom Crockett
1
Tego rodzaju ograniczenie wydaje się być wyraźnym wskaźnikiem bezużyteczności bezpieczeństwa typu bardziej niż cokolwiek innego. Przypomina mi cytat „pusty zestaw ma wiele ciekawych właściwości”.
ely
58
Możesz to zrobić za pomocą ekstraktorów scala i dopasowywania wzorców ( link ):
val x = List(1, 2, 3)
val t = x match {
caseList(a, b, c) => (a, b, c)
}
Co zwraca krotkę
t: (Int, Int, Int) = (1,2,3)
Możesz także użyć operatora wieloznacznego, jeśli nie masz pewności co do rozmiaru listy
val t = x match {
caseList(a, b, c, _*) => (a, b, c)
}
W kontekście tego rozwiązania skargi dotyczące bezpieczeństwa typów oznaczają, że możesz otrzymać błąd MatchError w czasie wykonywania. Jeśli chcesz, możesz spróbować złapać ten błąd i zrobić coś, jeśli lista ma złą długość. Kolejną fajną cechą tego rozwiązania jest to, że wynik nie musi być krotką, może to być jedna z własnych niestandardowych klas lub transformacja liczb. Nie sądzę jednak, aby można było to zrobić z elementami innymi niż Listy (więc może być konieczne wykonanie operacji .toList na wejściu).
Jim Pivarski,
Możesz to zrobić z dowolnym typem sekwencji Scala, używając składni +:. Nie zapomnij też dodać catch-all
import shapeless._
import syntax.std.traversable._
val x = List(1, 2, 3)
val xHList = x.toHList[Int::Int::Int::HNil]
val t = xHList.get.tupled
Uwaga: kompilator potrzebuje pewnych informacji o typie, aby przekonwertować List w HList, to powód, dla którego musisz przekazać informacje o typie do toHListmetody
Shapeless 2.0 zmienił część składni. Oto zaktualizowane rozwiązanie wykorzystujące bezkształtne.
import shapeless._
importHList._
import syntax.std.traversable._
val x = List(1, 2, 3)
val y = x.toHList[Int::Int::Int::HNil]
val z = y.get.tupled
Głównym problemem jest to, że typ .toHList należy określić z wyprzedzeniem. Mówiąc bardziej ogólnie, ponieważ liczba krotek jest ograniczona, projekt oprogramowania może być lepiej obsługiwany przez inne rozwiązanie.
Jeśli jednak tworzysz listę statycznie, rozważ rozwiązanie takie jak to, również wykorzystujące bezkształtne. Tutaj tworzymy HList bezpośrednio, a typ jest dostępny w czasie kompilacji. Pamiętaj, że lista HList zawiera funkcje zarówno z typów List, jak i Tuple. tj. może mieć elementy o różnych typach, takich jak krotka, i może być odwzorowywany wśród innych operacji, takich jak standardowe kolekcje. HLists zajmuje trochę czasu, zanim się przyzwyczaisz, więc idź powoli, jeśli jesteś nowy.
@javadba Szukałem, jak zaimplementować listToTuple (), ale ostatecznie nie musiałem tego robić. Lista cukru syntaktycznego rozwiązała mój problem.
Peter L
Jest jeszcze inna składnia, która moim zdaniem wygląda nieco lepiej:val List(c1, c2, c3) = myList
Kolmar
7
Nie możesz tego zrobić w bezpieczny sposób. W Scali listy to sekwencje elementów pewnego typu o dowolnej długości . O ile system typów wie,x może to być lista o dowolnej długości.
W przeciwieństwie do tego, liczba krotek musi być znana w czasie kompilacji. Naruszałoby to gwarancje bezpieczeństwa systemu typów, aby umożliwić przypisywaniex do typu krotki.
Byłoby możliwe ręczne zakodowanie funkcji, która konwertuje listy składające się z maksymalnie 22 elementów i zgłasza wyjątek dla większych list. Obsługa szablonów Scala, nadchodząca funkcja, uczyni to bardziej zwięzłym. Ale to byłby brzydki hack.
Jest bezpieczny dla typu: otrzymujesz None, jeśli rozmiar krotki jest nieprawidłowy, ale rozmiar krotki musi być literałem lub final val(aby można było zamienić na shapeless.Nat).
Wydaje się, że to nie działa dla rozmiarów większych niż 22, przynajmniej dla bezkształtnych_2.11: 2.3.3
kfkhalili
@kfkhalili Tak, i to nie jest bezkształtne ograniczenie. Sama Scala 2 nie zezwala na krotki z więcej niż 22 elementami, więc nie jest możliwe przekonwertowanie listy o większym rozmiarze na krotkę przy użyciu żadnej metody.
Kolmar
4
Korzystanie z dopasowania wzorców:
val intTuple = List (1,2,3) match {case List (a, b, c) => (a, b, c)}
Odpowiadając na tak stare (ponad 6-letnie) pytanie, często dobrym pomysłem jest wskazanie, w jaki sposób Twoja odpowiedź różni się od wszystkich (12) starszych odpowiedzi, które zostały już przesłane. W szczególności twoja odpowiedź ma zastosowanie tylko wtedy, gdy długość List/ Tuplejest znaną stałą.
jwvh
Dziękuję @ jwvh, rozważymy Twoje komentarze w przyszłości.
Michael Olafisoye
2
Post 2015. Aby odpowiedź Toma Crocketta była bardziej klarowna , oto prawdziwy przykład.
Na początku byłem zdezorientowany. Ponieważ pochodzę z Pythona, w którym możesz to zrobić tuple(list(1,2,3)).
Czy brakuje języka Scala? (odpowiedź brzmi - nie chodzi o Scala czy Python, chodzi o typ statyczny i typ dynamiczny).
To sprawia, że próbuję znaleźć sedno, dlaczego Scala nie może tego zrobić.
Poniższy przykład kodu implementuje toTuplemetodę, która ma bezpieczny toTupleNtyp i niebezpieczny typ toTuple.
toTupleSposób uzyskać informacje rodzaj długości w czasie wykonywania, czyli żadnych informacji typu długości w czasie kompilacji, więc typ zwracany jest Product, który jest bardzo podobny pytona jest tuplerzeczywiście (bez typu w każdej pozycji, a nie długości typów).
W ten sposób jest wymawiane na błąd czasu wykonywania, taki jak niezgodność typów lubIndexOutOfBoundException . (więc wygodna lista do krotki w Pythonie nie jest darmowym lunchem).
Wręcz przeciwnie, to długość informacji podanych przez użytkownika zapewnia toTupleNbezpieczeństwo kompilacji.
implicitclassEnrichedWithToTuple[A](elements: Seq[A]) {
deftoTuple: Product = elements.length match {
case2 => toTuple2
case3 => toTuple3
}
deftoTuple2= elements match {caseSeq(a, b) => (a, b) }
deftoTuple3= elements match {caseSeq(a, b, c) => (a, b, c) }
}
val product = List(1, 2, 3).toTuple
product.productElement(5) //runtime IndexOutOfBoundException, Bad ! val tuple = List(1, 2, 3).toTuple3
tuple._5 //compiler error, Good!
def toTuple(x: List[Int]): R
, jaki powinien być typ R?x match { case a :: b :: c :: Nil => (a, b, c); case _ => (0, 0, 0) }
i zwróć uwagę, że wynikowy typ jest ustalony naTuple3[Int,Int,Int]
List
, możesz przyjrzeć się typowi bezkształtnemuHList
, który umożliwia konwersję do iz krotek ( github.com/milessabin/ ... ). Może ma to zastosowanie do twojego przypadku użycia.Odpowiedzi:
Nie możesz tego zrobić w bezpieczny sposób. Czemu? Ponieważ ogólnie nie możemy znać długości listy do czasu wykonania. Ale „długość” krotki musi być zakodowana w jej typie, a zatem znana w czasie kompilacji. Na przykład
(1,'a',true)
ma typ(Int, Char, Boolean)
, dla którego jest cukierTuple3[Int, Char, Boolean]
. Powodem, dla którego krotki mają to ograniczenie, jest to, że muszą być w stanie obsłużyć niejednorodne typy.źródło
case class Vec3[A](_1: A, _2: A, _3: A)
map
działa w nim itp.Możesz to zrobić za pomocą ekstraktorów scala i dopasowywania wzorców ( link ):
val x = List(1, 2, 3) val t = x match { case List(a, b, c) => (a, b, c) }
Co zwraca krotkę
t: (Int, Int, Int) = (1,2,3)
Możesz także użyć operatora wieloznacznego, jeśli nie masz pewności co do rozmiaru listy
val t = x match { case List(a, b, c, _*) => (a, b, c) }
źródło
przykład użycia bezkształtnego :
import shapeless._ import syntax.std.traversable._ val x = List(1, 2, 3) val xHList = x.toHList[Int::Int::Int::HNil] val t = xHList.get.tupled
Uwaga: kompilator potrzebuje pewnych informacji o typie, aby przekonwertować List w HList, to powód, dla którego musisz przekazać informacje o typie do
toHList
metodyźródło
Shapeless 2.0 zmienił część składni. Oto zaktualizowane rozwiązanie wykorzystujące bezkształtne.
import shapeless._ import HList._ import syntax.std.traversable._ val x = List(1, 2, 3) val y = x.toHList[Int::Int::Int::HNil] val z = y.get.tupled
Głównym problemem jest to, że typ .toHList należy określić z wyprzedzeniem. Mówiąc bardziej ogólnie, ponieważ liczba krotek jest ograniczona, projekt oprogramowania może być lepiej obsługiwany przez inne rozwiązanie.
Jeśli jednak tworzysz listę statycznie, rozważ rozwiązanie takie jak to, również wykorzystujące bezkształtne. Tutaj tworzymy HList bezpośrednio, a typ jest dostępny w czasie kompilacji. Pamiętaj, że lista HList zawiera funkcje zarówno z typów List, jak i Tuple. tj. może mieć elementy o różnych typach, takich jak krotka, i może być odwzorowywany wśród innych operacji, takich jak standardowe kolekcje. HLists zajmuje trochę czasu, zanim się przyzwyczaisz, więc idź powoli, jeśli jesteś nowy.
scala> import shapeless._ import shapeless._ scala> import HList._ import HList._ scala> val hlist = "z" :: 6 :: "b" :: true :: HNil hlist: shapeless.::[String,shapeless.::[Int,shapeless.::[String,shapeless.::[Boolean,shapeless.HNil]]]] = z :: 6 :: b :: true :: HNil scala> val tup = hlist.tupled tup: (String, Int, String, Boolean) = (z,6,b,true) scala> tup res0: (String, Int, String, Boolean) = (z,6,b,true)
źródło
Pomimo prostoty i braku list o dowolnej długości, jest bezpieczny, a odpowiedź w większości przypadków:
val list = List('a','b') val tuple = list(0) -> list(1) val list = List('a','b','c') val tuple = (list(0), list(1), list(2))
Inna możliwość, gdy nie chcesz nazywać listy ani jej powtarzać (mam nadzieję, że ktoś może wskazać sposób na uniknięcie części Seq / head):
val tuple = Seq(List('a','b')).map(tup => tup(0) -> tup(1)).head val tuple = Seq(List('a','b','c')).map(tup => (tup(0), tup(1), tup(2))).head
źródło
FWIW, chciałem użyć krotki, aby zainicjalizować kilka pól i chciałem użyć cukru składniowego przypisania krotki. NA PRZYKŁAD:
val (c1, c2, c3) = listToTuple(myList)
Okazuje się, że jest też cukier syntaktyczny do przypisywania zawartości listy ...
val c1 :: c2 :: c3 :: Nil = myList
Więc nie ma potrzeby stosowania krotek, jeśli masz ten sam problem.
źródło
val List(c1, c2, c3) = myList
Nie możesz tego zrobić w bezpieczny sposób. W Scali listy to sekwencje elementów pewnego typu o dowolnej długości . O ile system typów wie,
x
może to być lista o dowolnej długości.W przeciwieństwie do tego, liczba krotek musi być znana w czasie kompilacji. Naruszałoby to gwarancje bezpieczeństwa systemu typów, aby umożliwić przypisywanie
x
do typu krotki.W rzeczywistości, z przyczyn technicznych, krotki Scala były ograniczone do 22 elementów , ale limit nie istnieje już w 2.11. Limit klasy przypadku został zniesiony w 2.11 https://github.com/scala/scala/pull/2305
Byłoby możliwe ręczne zakodowanie funkcji, która konwertuje listy składające się z maksymalnie 22 elementów i zgłasza wyjątek dla większych list. Obsługa szablonów Scala, nadchodząca funkcja, uczyni to bardziej zwięzłym. Ale to byłby brzydki hack.
źródło
Jeśli jesteś pewien, że twoja lista.rozmiar <23, użyj jej:
def listToTuple[A <: Object](list:List[A]):Product = { val class = Class.forName("scala.Tuple" + list.size) class.getConstructors.apply(0).newInstance(list:_*).asInstanceOf[Product] } listToTuple: [A <: java.lang.Object](list: List[A])Product scala> listToTuple(List("Scala", "Smart")) res15: Product = (Scala,Smart)
źródło
Można to również zrobić
shapeless
przy mniejszym schemacie, używającSized
:scala> import shapeless._ scala> import shapeless.syntax.sized._ scala> val x = List(1, 2, 3) x: List[Int] = List(1, 2, 3) scala> x.sized(3).map(_.tupled) res1: Option[(Int, Int, Int)] = Some((1,2,3))
Jest bezpieczny dla typu: otrzymujesz
None
, jeśli rozmiar krotki jest nieprawidłowy, ale rozmiar krotki musi być literałem lubfinal val
(aby można było zamienić nashapeless.Nat
).źródło
Korzystanie z dopasowania wzorców:
val intTuple = List (1,2,3) match {case List (a, b, c) => (a, b, c)}
źródło
List
/Tuple
jest znaną stałą.Post 2015. Aby odpowiedź Toma Crocketta była bardziej klarowna , oto prawdziwy przykład.
Na początku byłem zdezorientowany. Ponieważ pochodzę z Pythona, w którym możesz to zrobić
tuple(list(1,2,3))
.Czy brakuje języka Scala? (odpowiedź brzmi - nie chodzi o Scala czy Python, chodzi o typ statyczny i typ dynamiczny).
To sprawia, że próbuję znaleźć sedno, dlaczego Scala nie może tego zrobić.
Poniższy przykład kodu implementuje
toTuple
metodę, która ma bezpiecznytoTupleN
typ i niebezpieczny typtoTuple
.toTuple
Sposób uzyskać informacje rodzaj długości w czasie wykonywania, czyli żadnych informacji typu długości w czasie kompilacji, więc typ zwracany jestProduct
, który jest bardzo podobny pytona jesttuple
rzeczywiście (bez typu w każdej pozycji, a nie długości typów).W ten sposób jest wymawiane na błąd czasu wykonywania, taki jak niezgodność typów lub
IndexOutOfBoundException
. (więc wygodna lista do krotki w Pythonie nie jest darmowym lunchem).Wręcz przeciwnie, to długość informacji podanych przez użytkownika zapewnia
toTupleN
bezpieczeństwo kompilacji.implicit class EnrichedWithToTuple[A](elements: Seq[A]) { def toTuple: Product = elements.length match { case 2 => toTuple2 case 3 => toTuple3 } def toTuple2 = elements match {case Seq(a, b) => (a, b) } def toTuple3 = elements match {case Seq(a, b, c) => (a, b, c) } } val product = List(1, 2, 3).toTuple product.productElement(5) //runtime IndexOutOfBoundException, Bad ! val tuple = List(1, 2, 3).toTuple3 tuple._5 //compiler error, Good!
źródło
możesz to zrobić
iterując listę i stosując każdy element po kolei.
val xs: Seq[Any] = List(1:Int, 2.0:Double, "3":String) val t: (Int,Double,String) = xs.foldLeft((Tuple3[Int,Double,String] _).curried:Any)({ case (f,x) => f.asInstanceOf[Any=>Any](x) }).asInstanceOf[(Int,Double,String)]
źródło
o ile masz typ:
val x: List[Int] = List(1, 2, 3) def doSomething(a:Int *) doSomething(x:_*)
źródło