No es posible (excepto "a mano") AFAIK. Dado def toTuple(x: List[Int]): R, ¿cuál debería ser el tipo de R?
7
Si no es necesario hacerlo para una tupla de tamaño arbitrario (es decir, como un método hipotético presentado anteriormente), considere x match { case a :: b :: c :: Nil => (a, b, c); case _ => (0, 0, 0) }y tenga en cuenta que el tipo resultante se fija enTuple3[Int,Int,Int]
2
Si bien lo que busca hacer no es posible con List, podría buscar en el HListtipo Shapeless , que permite la conversión hacia y desde tuplas ( github.com/milessabin/… ). Quizás sea aplicable a su caso de uso.
Respuestas:
59
No puede hacer esto de forma segura. ¿Por qué? Porque, en general, no podemos saber la longitud de una lista hasta el tiempo de ejecución. Pero la "longitud" de una tupla debe codificarse en su tipo y, por tanto, conocerse en el momento de la compilación. Por ejemplo, (1,'a',true)tiene el tipo (Int, Char, Boolean), que es azúcar para Tuple3[Int, Char, Boolean]. La razón por la que las tuplas tienen esta restricción es que deben poder manejar tipos no homogéneos.
¿Existe alguna lista de elementos de tamaño fijo con el mismo tipo? No importando bibliotecas no estándar, como sin forma, por supuesto.
1
@davips no que yo sepa, pero es bastante fácil hacer el tuyo, por ejemplocase class Vec3[A](_1: A, _2: A, _3: A)
Tom Crockett
Esto sería una "tupla" de elementos con el mismo tipo, no puedo aplicarle mapa, por ejemplo.
1
@davips, como con cualquier tipo de datos nuevo, tendría que definir cómo, mapetc., funciona para él
Tom Crockett
1
Este tipo de limitación parece ser un indicador rotundo de la inutilidad de la seguridad de tipos más que cualquier otra cosa. Me recuerda a la cita "el conjunto vacío tiene muchas propiedades interesantes".
ely
58
Puede hacerlo usando extractores scala y coincidencia de patrones ( enlace ):
val x = List(1, 2, 3)
val t = x match {
caseList(a, b, c) => (a, b, c)
}
Que devuelve una tupla
t: (Int, Int, Int) = (1,2,3)
Además, puede utilizar un operador comodín si no está seguro del tamaño de la lista.
val t = x match {
caseList(a, b, c, _*) => (a, b, c)
}
En el contexto de esta solución, las quejas sobre la seguridad de tipos significan que es posible que obtenga un MatchError en tiempo de ejecución. Si lo desea, puede intentar detectar ese error y hacer algo si la lista tiene la longitud incorrecta. Otra característica interesante de esta solución es que la salida no tiene que ser una tupla, puede ser una de sus propias clases personalizadas o alguna transformación de los números. Sin embargo, no creo que pueda hacer esto con no listas (por lo que es posible que deba hacer una operación .toList en la entrada).
Jim Pivarski
Puede hacer esto con cualquier tipo de secuencia Scala usando la sintaxis + :. Además, no olvide agregar un catch-all
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
Nota: el compilador necesita alguna información de tipo para convertir la Lista en HList que la razón por la que necesita pasar información de tipo al toHListmétodo
Shapeless 2.0 cambió algo de sintaxis. Aquí está la solución actualizada usando shapeless.
import shapeless._
importHList._
import syntax.std.traversable._
val x = List(1, 2, 3)
val y = x.toHList[Int::Int::Int::HNil]
val z = y.get.tupled
El problema principal es que el tipo de .toHList debe especificarse con anticipación. De manera más general, dado que las tuplas son limitadas en su aridad, el diseño de su software podría estar mejor servido por una solución diferente.
Aún así, si está creando una lista de forma estática, considere una solución como esta, también usando sin forma. Aquí, creamos una HList directamente y el tipo está disponible en tiempo de compilación. Recuerde que una HList tiene características de los tipos List y Tuple. es decir, puede tener elementos con diferentes tipos como una tupla y se puede mapear entre otras operaciones como colecciones estándar. Sin embargo, las listas H tardan un poco en acostumbrarse, así que pise lentamente si es nuevo.
@javadba Estaba buscando cómo implementar listToTuple () pero terminé sin necesidad de hacerlo. La lista sintáctica del azúcar resolvió mi problema.
Peter L
También hay otra sintaxis, que en mi opinión se ve un poco mejor:val List(c1, c2, c3) = myList
Kolmar
7
No puede hacer esto de forma segura. En Scala, las listas son secuencias de elementos de algún tipo de longitud arbitraria . Por lo que sabe el sistema de tipos,x podría ser una lista de longitud arbitraria.
Por el contrario, la aridad de una tupla debe conocerse en el momento de la compilación. Violaría las garantías de seguridad del sistema de tipos para permitir la asignaciónx a un tipo de tupla.
Sería posible codificar manualmente una función que convierta listas de hasta 22 elementos y arroje una excepción para listas más grandes. El soporte de plantillas de Scala, una característica próxima, lo haría más conciso. Pero este sería un truco feo.
Es seguro para los tipos: obtiene None, si el tamaño de la tupla es incorrecto, pero el tamaño de la tupla debe ser literal o final val(para ser convertible a shapeless.Nat).
Esto no parece funcionar para tamaños superiores a 22, al menos para
shapeless_2.11
@kfkhalili Sí, y no es una limitación informe. Scala 2 en sí no permite tuplas con más de 22 elementos, por lo que no es posible convertir una Lista de mayor tamaño en una Tupla utilizando ningún método.
Kolmar
4
Uso de la coincidencia de patrones:
val intTuple = List (1,2,3) match {case List (a, b, c) => (a, b, c)}
Al responder una pregunta tan antigua (más de 6 años), a menudo es una buena idea señalar en qué se diferencia su respuesta de todas las (12) respuestas anteriores ya enviadas. En particular, su respuesta solo es aplicable si la longitud de List/ Tuplees una constante conocida.
jwvh
Gracias @ jwvh, considerará sus comentarios en el futuro.
Michael Olafisoye
2
Publicación de 2015. Para que la respuesta de Tom Crockett sea más aclaratoria , aquí hay un ejemplo real.
Al principio, me confundí. Porque vengo de Python, donde puedes hacerlo tuple(list(1,2,3)).
¿Le falta el lenguaje Scala? (la respuesta es: no se trata de Scala o Python, se trata de tipo estático y tipo dinámico).
Eso me lleva a tratar de encontrar el quid de por qué Scala no puede hacer esto.
El siguiente ejemplo de código implementa un toTuplemétodo, que tiene tipo seguro toTupleNy tipo inseguro toTuple.
El toTuplemétodo obtiene la información de longitud de tipo en tiempo de ejecución, es decir, no hay información de longitud de tipo en tiempo de compilación, por lo que el tipo de retorno Productes muy parecido al de Python tuple(sin tipo en cada posición y sin longitud de tipos).
De esa forma, se producirá un error de tiempo de ejecución como falta de coincidencia de tipos o IndexOutOfBoundException. (por lo que la conveniente lista a tupla de Python no es un almuerzo gratis).
Por el contrario, es la información de longitud proporcionada por el usuario la que hace que el toTupleNtiempo de compilación sea seguro.
implicitclassEnrichedWithToTuple[A](elements: Seq[A]) {
deftoTuple: Product = elements.length match {
case2 => toTuple2
case3 => toTuple3
}
deftoTuple2= elements match {caseSeq(a, b) => (a, b) }
deftoTuple3= elements match {caseSeq(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!
def toTuple(x: List[Int]): R
, ¿cuál debería ser el tipo de R?x match { case a :: b :: c :: Nil => (a, b, c); case _ => (0, 0, 0) }
y tenga en cuenta que el tipo resultante se fija enTuple3[Int,Int,Int]
List
, podría buscar en elHList
tipo Shapeless , que permite la conversión hacia y desde tuplas ( github.com/milessabin/… ). Quizás sea aplicable a su caso de uso.Respuestas:
No puede hacer esto de forma segura. ¿Por qué? Porque, en general, no podemos saber la longitud de una lista hasta el tiempo de ejecución. Pero la "longitud" de una tupla debe codificarse en su tipo y, por tanto, conocerse en el momento de la compilación. Por ejemplo,
(1,'a',true)
tiene el tipo(Int, Char, Boolean)
, que es azúcar paraTuple3[Int, Char, Boolean]
. La razón por la que las tuplas tienen esta restricción es que deben poder manejar tipos no homogéneos.fuente
case class Vec3[A](_1: A, _2: A, _3: A)
map
etc., funciona para élPuede hacerlo usando extractores scala y coincidencia de patrones ( enlace ):
val x = List(1, 2, 3) val t = x match { case List(a, b, c) => (a, b, c) }
Que devuelve una tupla
t: (Int, Int, Int) = (1,2,3)
Además, puede utilizar un operador comodín si no está seguro del tamaño de la lista.
val t = x match { case List(a, b, c, _*) => (a, b, c) }
fuente
un ejemplo usando 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
Nota: el compilador necesita alguna información de tipo para convertir la Lista en HList que la razón por la que necesita pasar información de tipo al
toHList
métodofuente
Shapeless 2.0 cambió algo de sintaxis. Aquí está la solución actualizada usando shapeless.
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
El problema principal es que el tipo de .toHList debe especificarse con anticipación. De manera más general, dado que las tuplas son limitadas en su aridad, el diseño de su software podría estar mejor servido por una solución diferente.
Aún así, si está creando una lista de forma estática, considere una solución como esta, también usando sin forma. Aquí, creamos una HList directamente y el tipo está disponible en tiempo de compilación. Recuerde que una HList tiene características de los tipos List y Tuple. es decir, puede tener elementos con diferentes tipos como una tupla y se puede mapear entre otras operaciones como colecciones estándar. Sin embargo, las listas H tardan un poco en acostumbrarse, así que pise lentamente si es nuevo.
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)
fuente
A pesar de la simplicidad y no ser para listas de ninguna longitud, es de tipo seguro y la respuesta en la mayoría de los casos:
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))
Otra posibilidad, cuando no quiere nombrar la lista ni repetirla (espero que alguien pueda mostrar una forma de evitar las partes de 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
fuente
FWIW, quería una tupla para inicializar varios campos y quería usar el azúcar sintáctico de la asignación de tuplas. P.EJ:
val (c1, c2, c3) = listToTuple(myList)
Resulta que también hay azúcar sintáctico para asignar el contenido de una lista ...
val c1 :: c2 :: c3 :: Nil = myList
Por lo tanto, no es necesario utilizar tuplas si tiene el mismo problema.
fuente
val List(c1, c2, c3) = myList
No puede hacer esto de forma segura. En Scala, las listas son secuencias de elementos de algún tipo de longitud arbitraria . Por lo que sabe el sistema de tipos,
x
podría ser una lista de longitud arbitraria.Por el contrario, la aridad de una tupla debe conocerse en el momento de la compilación. Violaría las garantías de seguridad del sistema de tipos para permitir la asignación
x
a un tipo de tupla.De hecho, por razones técnicas, las tuplas de Scala se limitaron a 22 elementos , pero el límite ya no existe en 2.11. El límite de clases de casos se eliminó en 2.11. https://github.com/scala/scala/pull/2305
Sería posible codificar manualmente una función que convierta listas de hasta 22 elementos y arroje una excepción para listas más grandes. El soporte de plantillas de Scala, una característica próxima, lo haría más conciso. Pero este sería un truco feo.
fuente
Si está muy seguro de que su list.size <23 úselo:
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)
fuente
Esto también se puede hacer
shapeless
con menos repetición usandoSized
: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))
Es seguro para los tipos: obtiene
None
, si el tamaño de la tupla es incorrecto, pero el tamaño de la tupla debe ser literal ofinal val
(para ser convertible ashapeless.Nat
).fuente
Uso de la coincidencia de patrones:
val intTuple = List (1,2,3) match {case List (a, b, c) => (a, b, c)}
fuente
List
/Tuple
es una constante conocida.Publicación de 2015. Para que la respuesta de Tom Crockett sea más aclaratoria , aquí hay un ejemplo real.
Al principio, me confundí. Porque vengo de Python, donde puedes hacerlo
tuple(list(1,2,3))
.¿Le falta el lenguaje Scala? (la respuesta es: no se trata de Scala o Python, se trata de tipo estático y tipo dinámico).
Eso me lleva a tratar de encontrar el quid de por qué Scala no puede hacer esto.
El siguiente ejemplo de código implementa un
toTuple
método, que tiene tipo segurotoTupleN
y tipo insegurotoTuple
.El
toTuple
método obtiene la información de longitud de tipo en tiempo de ejecución, es decir, no hay información de longitud de tipo en tiempo de compilación, por lo que el tipo de retornoProduct
es muy parecido al de Pythontuple
(sin tipo en cada posición y sin longitud de tipos).De esa forma, se producirá un error de tiempo de ejecución como falta de coincidencia de tipos o
IndexOutOfBoundException
. (por lo que la conveniente lista a tupla de Python no es un almuerzo gratis).Por el contrario, es la información de longitud proporcionada por el usuario la que hace que el
toTupleN
tiempo de compilación sea seguro.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!
fuente
puedes hacer esto
iterando a través de la lista y aplicando cada elemento uno por uno.
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)]
fuente
hasta donde tenga el tipo:
val x: List[Int] = List(1, 2, 3) def doSomething(a:Int *) doSomething(x:_*)
fuente