Ce n'est pas possible (sauf "à la main") AFAIK. Étant donné def toTuple(x: List[Int]): R, quel devrait être le type de R?
7
Si cela n'a pas besoin d'être fait pour un tuple de taille arbitraire (c'est-à-dire comme une méthode hypothétique présentée ci-dessus), considérez x match { case a :: b :: c :: Nil => (a, b, c); case _ => (0, 0, 0) }et notez que le type résultant est fixé àTuple3[Int,Int,Int]
2
Bien que ce que vous cherchez à faire n'est pas possible avec List, vous pouvez regarder dans le HListtype Shapeless ' , qui permet la conversion vers et depuis les tuples ( github.com/milessabin/… ). Peut-être que cela s'applique à votre cas d'utilisation.
Réponses:
59
Vous ne pouvez pas faire cela de manière sécurisée. Pourquoi? Parce qu'en général, nous ne pouvons pas connaître la longueur d'une liste avant l'exécution. Mais la "longueur" d'un tuple doit être encodée dans son type, et donc connue au moment de la compilation. Par exemple, (1,'a',true)a le type (Int, Char, Boolean), qui est le sucre Tuple3[Int, Char, Boolean]. La raison pour laquelle les tuples ont cette restriction est qu'ils doivent pouvoir gérer des types non homogènes.
Existe-t-il une liste d'éléments de taille fixe avec le même type? Ne pas importer de bibliothèques non standard, comme informes, bien sûr.
1
@davips pas à ma connaissance, mais c'est assez facile de créer le vôtre, par exemplecase class Vec3[A](_1: A, _2: A, _3: A)
Tom Crockett
Ce serait un "tuple" d'éléments du même type, je ne peux pas y appliquer de carte, par exemple.
1
@davips comme avec tout nouveau type de données, vous devrez définir comment mapetc fonctionne pour cela
Tom Crockett
1
Ce genre de limitation semble être un indicateur retentissant de l'inutilité de la sécurité de type plus que toute autre chose. Cela me rappelle la citation «l'ensemble vide a de nombreuses propriétés intéressantes».
ely
58
Vous pouvez le faire en utilisant des extracteurs scala et des correspondances de modèles ( lien ):
val x = List(1, 2, 3)
val t = x match {
caseList(a, b, c) => (a, b, c)
}
Qui renvoie un tuple
t: (Int, Int, Int) = (1,2,3)
En outre, vous pouvez utiliser un opérateur générique si vous n'êtes pas sûr de la taille de la liste
val t = x match {
caseList(a, b, c, _*) => (a, b, c)
}
Dans le contexte de cette solution, les plaintes concernant la sécurité de type signifient que vous pourriez obtenir une MatchError au moment de l'exécution. Si vous le souhaitez, vous pouvez essayer de détecter cette erreur et faire quelque chose si la liste a la mauvaise longueur. Une autre fonctionnalité intéressante de cette solution est que la sortie ne doit pas nécessairement être un tuple, cela peut être l'une de vos propres classes personnalisées ou une transformation des nombres. Je ne pense pas que vous puissiez le faire avec des non-Lists, cependant (vous devrez peut-être faire une opération .toList à l'entrée).
Jim Pivarski du
Vous pouvez le faire avec n'importe quel type de séquence Scala en utilisant la syntaxe +:. Aussi, n'oubliez pas d'ajouter un fourre-tout
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
Remarque: le compilateur a besoin d'informations de type pour convertir la liste dans la liste HList que la raison pour laquelle vous devez passer des informations de type à la toHListméthode
Shapeless 2.0 a changé une partie de la syntaxe. Voici la solution mise à jour utilisant informe.
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
Le principal problème étant que le type de .toHList doit être spécifié à l'avance. Plus généralement, comme les tuples sont limités dans leur arité, la conception de votre logiciel peut être mieux servie par une solution différente.
Néanmoins, si vous créez une liste de manière statique, envisagez une solution comme celle-ci, utilisant également l'informe. Ici, nous créons directement une HList et le type est disponible au moment de la compilation. N'oubliez pas qu'une HList a des fonctionnalités des types List et Tuple. c'est-à-dire qu'il peut avoir des éléments avec différents types comme un Tuple et peut être mappé parmi d'autres opérations comme des collections standard. Les HLists prennent un peu de temps pour s'y habituer, alors avancez lentement si vous êtes nouveau.
Malgré la simplicité et n'étant pas pour les listes de toute longueur, il est de type sécurisé et la réponse dans la plupart des cas:
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))
Autre possibilité, quand vous ne voulez pas nommer la liste ni la répéter (j'espère que quelqu'un pourra montrer un moyen d'éviter les parties 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
FWIW, je voulais un tuple pour initialiser un certain nombre de champs et je voulais utiliser le sucre syntaxique de l'affectation de tuple. PAR EXEMPLE:
val (c1, c2, c3) = listToTuple(myList)
Il s'avère qu'il existe également du sucre syntaxique pour attribuer le contenu d'une liste ...
val c1 :: c2 :: c3 :: Nil = myList
Donc pas besoin de tuples si vous avez le même problème.
@javadba Je cherchais comment implémenter listToTuple () mais j'ai fini par ne pas en avoir besoin. La liste de sucre syntaxique a résolu mon problème.
Peter L du
Il y a aussi une autre syntaxe, qui à mon avis semble un peu meilleure:val List(c1, c2, c3) = myList
Kolmar
7
Vous ne pouvez pas faire cela de manière sécurisée. Dans Scala, les listes sont des séquences de longueur arbitraire d'éléments d'un certain type. Pour autant que le système de typage le sache,x pourrait être une liste de longueur arbitraire.
En revanche, l'arité d'un tuple doit être connue au moment de la compilation. Cela violerait les garanties de sécurité du système de type pour permettre l'attributionx à un type de tuple.
Il serait possible de coder manuellement une fonction qui convertit des listes de jusqu'à 22 éléments et lève une exception pour les listes plus grandes. La prise en charge des modèles de Scala, une fonctionnalité à venir, rendrait cela plus concis. Mais ce serait un horrible hack.
Il est de type sécurisé: vous obtenez None, si la taille du tuple est incorrecte, mais la taille du tuple doit être un littéral ou final val(pour être convertible en shapeless.Nat).
Cela ne semble pas fonctionner pour les tailles supérieures à 22, du moins pour les
shapeless_2.11
@kfkhalili Oui, et ce n'est pas une limitation informe. Scala 2 lui-même n'autorise pas les tuples avec plus de 22 éléments, il n'est donc pas possible de convertir une liste de plus grande taille en un tuple en utilisant n'importe quelle méthode.
Kolmar
4
Utilisation de la correspondance de motifs:
val intTuple = List (1,2,3) match {case List (a, b, c) => (a, b, c)}
Lorsque vous répondez à une question aussi ancienne (plus de 6 ans), c'est souvent une bonne idée de souligner en quoi votre réponse est différente de toutes les (12) réponses plus anciennes déjà soumises. En particulier, votre réponse n'est applicable que si la longueur du List/ Tupleest une constante connue.
jwvh
Merci @ jwvh, examinera vos commentaires à l'avenir.
Michael Olafisoye
2
Après 2015. Pour que la réponse de Tom Crockett soit plus claire , voici un exemple concret.
Au début, j'étais confus à ce sujet. Parce que je viens de Python, où vous pouvez simplement le faire tuple(list(1,2,3)).
Est-ce à court de langage Scala? (la réponse est - il ne s'agit pas de Scala ou de Python, mais de type statique et de type dynamique.)
Cela me pousse à essayer de trouver la raison pour laquelle Scala ne peut pas faire cela.
L'exemple de code suivant implémente une toTupleméthode, qui a type-safe toTupleNet type-unsafe toTuple.
La toTupleméthode obtient les informations de longueur de type au moment de l'exécution, c'est-à-dire aucune information de longueur de type au moment de la compilation, donc le type de retour Productest très similaire à celui de Python tuple(pas de type à chaque position, et pas de longueur de types).
Cette façon est prononcée à une erreur d'exécution comme type-mismatch ou IndexOutOfBoundException. (Ainsi, la liste à tuple pratique de Python n'est pas un déjeuner gratuit.)
Au contraire, ce sont les informations de longueur fournies par l'utilisateur qui rendent la toTupleNcompilation sûre.
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
, quel devrait être le type de R?x match { case a :: b :: c :: Nil => (a, b, c); case _ => (0, 0, 0) }
et notez que le type résultant est fixé àTuple3[Int,Int,Int]
List
, vous pouvez regarder dans leHList
type Shapeless ' , qui permet la conversion vers et depuis les tuples ( github.com/milessabin/… ). Peut-être que cela s'applique à votre cas d'utilisation.Réponses:
Vous ne pouvez pas faire cela de manière sécurisée. Pourquoi? Parce qu'en général, nous ne pouvons pas connaître la longueur d'une liste avant l'exécution. Mais la "longueur" d'un tuple doit être encodée dans son type, et donc connue au moment de la compilation. Par exemple,
(1,'a',true)
a le type(Int, Char, Boolean)
, qui est le sucreTuple3[Int, Char, Boolean]
. La raison pour laquelle les tuples ont cette restriction est qu'ils doivent pouvoir gérer des types non homogènes.la source
case class Vec3[A](_1: A, _2: A, _3: A)
map
etc fonctionne pour celaVous pouvez le faire en utilisant des extracteurs scala et des correspondances de modèles ( lien ):
val x = List(1, 2, 3) val t = x match { case List(a, b, c) => (a, b, c) }
Qui renvoie un tuple
t: (Int, Int, Int) = (1,2,3)
En outre, vous pouvez utiliser un opérateur générique si vous n'êtes pas sûr de la taille de la liste
val t = x match { case List(a, b, c, _*) => (a, b, c) }
la source
un exemple utilisant informe :
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
Remarque: le compilateur a besoin d'informations de type pour convertir la liste dans la liste HList que la raison pour laquelle vous devez passer des informations de type à la
toHList
méthodela source
Shapeless 2.0 a changé une partie de la syntaxe. Voici la solution mise à jour utilisant informe.
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
Le principal problème étant que le type de .toHList doit être spécifié à l'avance. Plus généralement, comme les tuples sont limités dans leur arité, la conception de votre logiciel peut être mieux servie par une solution différente.
Néanmoins, si vous créez une liste de manière statique, envisagez une solution comme celle-ci, utilisant également l'informe. Ici, nous créons directement une HList et le type est disponible au moment de la compilation. N'oubliez pas qu'une HList a des fonctionnalités des types List et Tuple. c'est-à-dire qu'il peut avoir des éléments avec différents types comme un Tuple et peut être mappé parmi d'autres opérations comme des collections standard. Les HLists prennent un peu de temps pour s'y habituer, alors avancez lentement si vous êtes nouveau.
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)
la source
Malgré la simplicité et n'étant pas pour les listes de toute longueur, il est de type sécurisé et la réponse dans la plupart des cas:
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))
Autre possibilité, quand vous ne voulez pas nommer la liste ni la répéter (j'espère que quelqu'un pourra montrer un moyen d'éviter les parties 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
la source
FWIW, je voulais un tuple pour initialiser un certain nombre de champs et je voulais utiliser le sucre syntaxique de l'affectation de tuple. PAR EXEMPLE:
val (c1, c2, c3) = listToTuple(myList)
Il s'avère qu'il existe également du sucre syntaxique pour attribuer le contenu d'une liste ...
val c1 :: c2 :: c3 :: Nil = myList
Donc pas besoin de tuples si vous avez le même problème.
la source
val List(c1, c2, c3) = myList
Vous ne pouvez pas faire cela de manière sécurisée. Dans Scala, les listes sont des séquences de longueur arbitraire d'éléments d'un certain type. Pour autant que le système de typage le sache,
x
pourrait être une liste de longueur arbitraire.En revanche, l'arité d'un tuple doit être connue au moment de la compilation. Cela violerait les garanties de sécurité du système de type pour permettre l'attribution
x
à un type de tuple.En fait, pour des raisons techniques, les tuples Scala étaient limités à 22 éléments , mais la limite n'existe plus en 2.11 La limite de classe de cas a été levée en 2.11 https://github.com/scala/scala/pull/2305
Il serait possible de coder manuellement une fonction qui convertit des listes de jusqu'à 22 éléments et lève une exception pour les listes plus grandes. La prise en charge des modèles de Scala, une fonctionnalité à venir, rendrait cela plus concis. Mais ce serait un horrible hack.
la source
Si vous êtes sûr que votre list.size <23 utilisez-le:
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)
la source
Cela peut également être fait
shapeless
avec moins de passe-partout en utilisantSized
: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))
Il est de type sécurisé: vous obtenez
None
, si la taille du tuple est incorrecte, mais la taille du tuple doit être un littéral oufinal val
(pour être convertible enshapeless.Nat
).la source
Utilisation de la correspondance de motifs:
val intTuple = List (1,2,3) match {case List (a, b, c) => (a, b, c)}
la source
List
/Tuple
est une constante connue.Après 2015. Pour que la réponse de Tom Crockett soit plus claire , voici un exemple concret.
Au début, j'étais confus à ce sujet. Parce que je viens de Python, où vous pouvez simplement le faire
tuple(list(1,2,3))
.Est-ce à court de langage Scala? (la réponse est - il ne s'agit pas de Scala ou de Python, mais de type statique et de type dynamique.)
Cela me pousse à essayer de trouver la raison pour laquelle Scala ne peut pas faire cela.
L'exemple de code suivant implémente une
toTuple
méthode, qui a type-safetoTupleN
et type-unsafetoTuple
.La
toTuple
méthode obtient les informations de longueur de type au moment de l'exécution, c'est-à-dire aucune information de longueur de type au moment de la compilation, donc le type de retourProduct
est très similaire à celui de Pythontuple
(pas de type à chaque position, et pas de longueur de types).Cette façon est prononcée à une erreur d'exécution comme type-mismatch ou
IndexOutOfBoundException
. (Ainsi, la liste à tuple pratique de Python n'est pas un déjeuner gratuit.)Au contraire, ce sont les informations de longueur fournies par l'utilisateur qui rendent la
toTupleN
compilation sûre.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!
la source
vous pouvez le faire soit
en parcourant la liste et en appliquant chaque élément un par un.
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)]
la source
dans la mesure où vous avez le type:
val x: List[Int] = List(1, 2, 3) def doSomething(a:Int *) doSomething(x:_*)
la source