A veces hay necesidad de crear tuplas a partir de colecciones pequeñas (por ejemplo, marco de escaldado).¿Hay forma de crear tupla desde la lista (sin generación de código)?
def toTuple(list:List[Any]):scala.Product = ...
A veces hay necesidad de crear tuplas a partir de colecciones pequeñas (por ejemplo, marco de escaldado).¿Hay forma de crear tupla desde la lista (sin generación de código)?
def toTuple(list:List[Any]):scala.Product = ...
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)
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)
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))
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 –
¿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): _*)
También deseando' Tuple', así que podría usar 'FunctionX.tupled', que de hecho requiere un' TupleX', no 'Producto' (nota incluso' ProductoX'). – metasim
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: _*)
}
}
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
1 Muy útil para limpiar asignación de cadenas. No funciona para var (a, b, c) = toTuple (myIter.toList) - ¿Alguna idea? –
Rubistro: para ese propósito puedes usar 'var List (a, b, c) = myIter.toList' –