2012-07-03 1 views

Répondre

9

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) 
+0

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? –

+1

Rubistro: à cet effet, vous pouvez utiliser 'var Liste (a, b, c) = myIter.toList' –

14

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) 
14

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)) 
+0

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 –

3

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): _*) 
+0

Souhaitant également' Tuple', donc je pourrais utiliser 'FunctionX.tupled', ce qui nécessite en effet un' TupleX', pas 'Product' (notez même' ProductX'). – metasim

1

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: _*) 
    } 

} 
+0

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

Questions connexes