2012-07-03 9 views

Respuesta

9

Si no conoce la aridad en la delantera y quiere hacer un terrible corte terribles, usted puede hacer esto:

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 Muy útil para limpiar asignación de cadenas. No funciona para var (a, b, c) = toTuple (myIter.toList) - ¿Alguna idea? –

+1

Rubistro: para ese propósito puedes usar 'var List (a, b, c) = myIter.toList' –

14

Usted realmente no quiere que su método para volver Product ya que se trata inútilmente vaga. Si quieres poder usar el objeto devuelto como una tupla, entonces tendrás que conocer su arity. Entonces, lo que puedes hacer es tener una serie de métodos toTupleN para diferentes arios. Para su comodidad, puede agregar estos como métodos implícitos en Seq.

¿Qué tal esto:

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) 

y usarlo como:

scala> List(1,2,3).toTuple3 
res0: (Int, Int, Int) = (1,2,3) 
14

Si, como @dhg observó, se conoce el aridad esperado en la delantera se puede hacer algo útil en este caso. El uso de shapeless se podría escribir,

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

Pero 'Shapeless' no proporciona la manera de crear una' 'Tuple' de list' si la longitud de la lista no está definido en tiempo de compilación, que puede conducir a errores de ejecución –

3

¿Quieres una Tuple o solo un Product. Debido a que para estos últimos:

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

También deseando' Tuple', así que podría usar 'FunctionX.tupled', que de hecho requiere un' TupleX', no 'Producto' (nota incluso' ProductoX'). – metasim

1

Sobre la base de la idea de @Kim Stebel, me escribió una sencilla utilidad que crea tupla de la SEC.

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

Gracias, @xKommando. Esto es justo lo que necesitaba. Como quería _toTuple_ aplicar a una secuencia de _Any_ en lugar de _AnyRef_, reemplacé el tipo de elems con _Seq [Cualquiera] _ y reemplacé la expresión para el caso 'size <= 22' con' val refs = for (e <- elems) yield e.asInstanceOf [AnyRef] ctors (size - 1) .newInstance (refs: _ *). asInstanceOf [Product] '(en función de la respuesta aquí: [link] (http://stackoverflow.com/questions)/16751484/scala-how-to-force-wrapping-an-integer-as-an-object)) – Phasmid

Cuestiones relacionadas