Il est parfois nécessaire de créer des tuples à partir de petites collections (par exemple, une structure d'échaudage).Est-il possible de créer un tuple à partir de la liste (sans codegeneration)?
def toTuple(list:List[Any]):scala.Product = ...
Il est parfois nécessaire de créer des tuples à partir de petites collections (par exemple, une structure d'échaudage).Est-il possible de créer un tuple à partir de la liste (sans codegeneration)?
def toTuple(list:List[Any]):scala.Product = ...
Si vous ne connaissez pas le arity à l'avant et que vous voulez faire une terrible affreuse magouille, vous pouvez le faire:
def toTuple[A <: Object](as:List[A]):Product = {
val tupleClass = Class.forName("scala.Tuple" + as.size)
tupleClass.getConstructors.apply(0).newInstance(as:_*).asInstanceOf[Product]
}
toTuple: [A <: java.lang.Object](as: List[A])Product
scala> toTuple(List("hello", "world"))
res15: Product = (hello,world)
Vous ne voulez vraiment pas votre méthode pour revenir Product
puisque c'est vague inutilement. Si vous voulez pouvoir utiliser l'objet retourné sous la forme d'un tuple, vous devrez en connaître l'arité. Donc, ce que vous pouvez faire est d'avoir une série de toTupleN
méthodes pour différentes arités. Pour plus de commodité, vous pouvez les ajouter en tant que méthodes implicites sur Seq
.
Que diriez-vous ceci:
class EnrichedWithToTuple[A](elements: Seq[A]) {
def toTuple2 = elements match { case Seq(a, b) => (a, b) }
def toTuple3 = elements match { case Seq(a, b, c) => (a, b, c) }
def toTuple4 = elements match { case Seq(a, b, c, d) => (a, b, c, d) }
def toTuple5 = elements match { case Seq(a, b, c, d, e) => (a, b, c, d, e) }
}
implicit def enrichWithToTuple[A](elements: Seq[A]) = new EnrichedWithToTuple(elements)
et l'utiliser comme:
scala> List(1,2,3).toTuple3
res0: (Int, Int, Int) = (1,2,3)
Si, comme @dhg observé, vous connaissez le arité prévu à l'avant, vous pouvez faire quelque chose d'utile ici. En utilisant shapeless vous pouvez écrire,
scala> import shapeless._
import shapeless._
scala> import Traversables._
import Traversables._
scala> import Tuples._
import Tuples._
scala> List(1, 2, 3).toHList[Int :: Int :: Int :: HNil] map tupled
res0: Option[(Int, Int, Int)] = Some((1,2,3))
Mais 'Shapeless' ne fournit pas la façon de créer un' 'Tuple' de list' si la longueur de la liste est non définie dans le temps de la compilation, il peut conduire à des erreurs d'exécution –
Voulez-vous un ou Tuple
juste un Product
. Parce que pour ce dernier:
case class SeqProduct[A](elems: A*) {
override def productArity: Int = elems.size
override def productElement(i: Int) = elems(i)
}
SeqProduct(List(1, 2, 3): _*)
Souhaitant également' Tuple', donc je pourrais utiliser 'FunctionX.tupled', ce qui nécessite en effet un' TupleX', pas 'Product' (notez même' ProductX'). – metasim
Basé sur l'idée de @Kim Stebel, j'ai écrit un utilitaire simple qui crée un tuple à partir de seq.
import java.lang.reflect.Constructor
/**
* Created by Bowen Cai on 1/24/2015.
*/
sealed trait Product0 extends Any with Product {
def productArity = 0
def productElement(n: Int) = throw new IllegalStateException("No element")
def canEqual(that: Any) = false
}
object Tuple0 extends Product0 {
override def toString() = "()"
}
case class SeqProduct(elems: Any*) extends Product {
override def productArity: Int = elems.size
override def productElement(i: Int) = elems(i)
override def toString() = elems.addString(new StringBuilder(elems.size * 8 + 10), "(" , ",", ")").toString()
}
object Tuples {
private[this] val ctors = {
val ab = Array.newBuilder[Constructor[_]]
for (i <- 1 to 22) {
val tupleClass = Class.forName("scala.Tuple" + i)
ab += tupleClass.getConstructors.apply(0)
}
ab.result()
}
def toTuple(elems: Seq[AnyRef]): Product = elems.length match {
case 0 => Tuple0
case size if size <= 22 =>
ctors(size - 1).newInstance(elems: _*).asInstanceOf[Product]
case size if size > 22 => new SeqProduct(elems: _*)
}
}
Merci, @xKommando. C'est exactement ce dont j'avais besoin. Parce que je voulais que _toTuple_ s'applique à une séquence de _Any_ plutôt que _AnyRef_, j'ai remplacé le type d'elems par _Seq [Any] _ et remplacé l'expression du cas 'size <= 22' par' val refs = for (e <- elems) céder e.asInstanceOf [AnyRef] ctors (taille - 1) .newInstance (refs: _ *). asInstanceOf [Product] '(basé sur la réponse ici: [link] (http://stackoverflow.com/questions/16751484/scala-how-to-force-wrapping-an-entier-comme-un-objet)) – Phasmid
1 Très utile pour le nettoyage affectation à partir de chaînes. Ca ne marche pas pour var (a, b, c) = toTuple (myIter.toList) - Des idées? –
Rubistro: à cet effet, vous pouvez utiliser 'var Liste (a, b, c) = myIter.toList' –