web-dev-qa-db-fra.com

Convertir une liste Scala en un tuple?

Comment puis-je convertir une liste avec (disons) 3 éléments en un tuple de taille 3?

Par exemple, supposons que j’ai val x = List(1, 2, 3) et que je veuille convertir ceci en (1, 2, 3). Comment puis-je faire ceci?

78
grautur

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 codé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 du sucre pour Tuple3[Int, Char, Boolean]. La raison pour laquelle les tuples ont cette restriction est qu'ils doivent être capables de gérer des types non homogènes.

50
Tom Crockett

Vous pouvez le faire en utilisant scala extracteurs et correspondance de modèles ( lien ):

val x = List(1, 2, 3)

val t = x match {
  case List(a, b, c) => (a, b, c)
}

Ce qui retourne 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)
}
47
cyrillk

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 indiquant la raison pour laquelle vous devez transmettre les informations de type à la méthode toHList

46
evantill

Shapeless 2. a modifié certaines syntaxes. Voici la solution mise à jour utilisant informeless.

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 problème principal est que le type de . ToHList doit être spécifié à l'avance. Plus généralement, comme les nuplets ont une arité limitée, la conception de votre logiciel pourrait ê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, qui utilise également une configuration sans forme. Ici, nous créons directement une HList et le type est disponible au moment de la compilation. Rappelez-vous qu'une HList a des fonctionnalités des types List et Tuple. c'est-à-dire qu'il peut avoir des éléments de différents types, comme un tuple, et qu'il peut être mappé entre d'autres opérations, telles que des collections standard. Il faut un peu de temps pour que les listes H s’y habituent, 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)
16
virtualirfan

En dépit de sa simplicité et de son absence pour les listes de toute longueur, il est 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))

Une autre possibilité, lorsque vous ne souhaitez 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
10
user445107

FWIW, je voulais un tuple pour initalise un certain nombre de champs et je voulais utiliser le sucre syntaxique de Tuple assign. PAR EXEMPLE:

val (c1, c2, c3) = listToTuple(myList)

Il s'avère qu'il existe un sucre syntaxique pour assigner le contenu d'une liste aussi ...

val c1 :: c2 :: c3 :: Nil = myList

Donc, pas besoin de tuples si vous avez le même problème.

9
Peter L

Vous ne pouvez pas le faire d'une manière sûre. En Scala, les listes sont des séquences de longueur arbitraire de types différents. Autant que le système de types 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. Il serait contraire aux garanties de sécurité du système de types d'autoriser l'affectation de x à un type de tuple.

En fait, pour des raisons techniques, Scala tuples était limité à 22 éléments , mais la limite n'existe plus dans 2.11. La limite de classe de cas a été levée dans 2.11 - https://github.com/scala/scala/pull/2305

Il serait possible de coder manuellement une fonction qui convertit des listes de 22 éléments maximum et lève une exception pour les listes plus volumineuses. Le support des modèles de Scala, une fonctionnalité à venir, rendrait ceci plus concis. Mais ce serait un bidule laid.

6
Mechanical snail

Cela peut aussi être fait dans shapeless avec moins de passe-passe 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))

C'est sûr pour le type: vous obtenez None, si la taille du tuple est incorrecte, mais la taille du tuple doit être littérale ou final val (être convertible en shapeless.Nat).

3
Kolmar

Si vous êtes vraiment sûr que votre liste list.size <23 l'utilise:

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)
2
Spark-Beginner

2015 post. Pour que la réponse de Tom Crockett soit plus clarifiant , voici un exemple réel.

Au début, je suis confus à ce sujet. Parce que je viens de Python, où vous pouvez simplement faire Tuple(list(1,2,3)).
Est-il court de Scala langue? (La réponse est - il ne s'agit pas de Scala ou de Python, il s'agit de type statique et dynamique -type.)

Cela me fait essayer de trouver le noeud pourquoi Scala ne peut pas faire cela.


L'exemple de code suivant implémente une méthode toTuple, qui a le type-safe toTupleN et le type-unsafe toTuple.

La méthode toTuple obtient les informations de type-longueur au moment de l'exécution, c'est-à-dire qu'il n'y a pas d'informations de type-longueur au moment de la compilation. Le type de résultat est donc Product, ce qui est très semblable au Tuple en effet (pas de type à chaque position et pas de longueur de type).
Cette façon de faire est prédite comme une erreur d’exécution telle que type-mismatch ou IndexOutOfBoundException. (Ainsi, la liste des numéros de téléphone de Python n'est pas un repas gratuit.)

Au contraire, c’est l’information de longueur fournie par l’utilisateur qui rend toTupleN sûr de la compilation.

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!
1
WeiChing Lin

vous pouvez le faire non plus

  1. via un filtrage par motif (ce que vous ne voulez pas) ou
  2. en parcourant la liste et en appliquant chaque élément un à 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)]
    
1
comonad

Utilisation de la correspondance de modèle:

val intTuple = List (1,2,3) match {case List (a, b, c) => (a, b, c)}

1

dans la mesure où vous avez le type:

val x: List[Int] = List(1, 2, 3)

def doSomething(a:Int *)

doSomething(x:_*)
1
dzs