Comment puis-je convertir une liste avec (disons) 3 éléments en un tuple de taille 3?
Par exemple, disons que j'ai val x = List(1, 2, 3)
et je veux convertir cela en (1, 2, 3)
. Comment puis-je faire ceci?
Comment puis-je convertir une liste avec (disons) 3 éléments en un tuple de taille 3?
Par exemple, disons que j'ai val x = List(1, 2, 3)
et je veux convertir cela en (1, 2, 3)
. Comment puis-je faire ceci?
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 le HList
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 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.
case class Vec3[A](_1: A, _2: A, _3: A)
map
etc fonctionne pour cela
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 {
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)
}
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éthode
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)
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.
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'attributionx
à 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.
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)
Cela peut également être fait shapeless
avec moins de passe-partout en utilisant Sized
:
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 ou final val
(pour être convertible en shapeless.Nat
).
Utilisation de la correspondance de motifs:
val intTuple = List (1,2,3) match {case List (a, b, c) => (a, b, c)}
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-safe toTupleN
et type-unsafe toTuple
.
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 retour Product
est 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 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!
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)]
def toTuple(x: List[Int]): R
, quel devrait être le type de R?