list - español - funciones en scala
Convierte una lista Scala en una tupla? (12)
A pesar de la simplicidad y no es para listas de cualquier longitud, es 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 quieres nombrar la lista ni repetirla (espero que alguien pueda mostrar una forma de evitar las partes de Seq / cabeza):
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
Editar: agregado soporte faltante
¿Cómo puedo convertir una lista con (por ejemplo) 3 elementos en una tupla de tamaño 3?
Por ejemplo, supongamos que tengo val x = List(1, 2, 3)
y quiero convertir esto en (1, 2, 3)
. ¿Cómo puedo hacer esto?
Esto también se puede hacer shapeless
con menos repetitivo utilizando Sized
:
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 tipos: obtienes None
, si el tamaño de la tupla es incorrecto, pero el tamaño de la tupla debe ser un valor literal o final val
(para ser convertible a shapeless.Nat
. shapeless.Nat
).
FWIW, quería una tupla para iniciar un número de 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áctica para asignar los contenidos de una lista ...
val c1 :: c2 :: c3 :: Nil = myList
Así que no hay necesidad de tuplas si tienes el mismo problema.
No puedes hacer esto de forma segura. En Scala, las listas son secuencias de longitud arbitraria de elementos de algún tipo. En lo que respecta al sistema de tipos, x
podría ser una lista de longitud arbitraria.
Por el contrario, la aria de una tupla debe conocerse en tiempo de compilación. Violaría las garantías de seguridad del sistema tipo para permitir la asignación de x
a un tipo de tupla.
De hecho, por razones técnicas, las tuplas de Scala estaban limitadas a 22 elementos , pero el límite ya no existe en 2.11 El límite de la clase de caso se ha eliminado 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 función próxima, lo haría más conciso. Pero esto sería un hack feo.
No puedes hacer esto de una manera segura. ¿Por qué? Porque, en general, no podemos conocer la longitud de una lista hasta el tiempo de ejecución. Pero la "longitud" de una tupla debe codificarse en su tipo y, por lo tanto, conocerse en tiempo de 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 cual las tuplas tienen esta restricción es que necesitan poder manejar tipos no homogéneos.
Publicación de 2015 Para que la respuesta de Tom Crockett sea más esclarecedora , aquí hay un ejemplo real.
Al principio, me confundí al respecto. Porque vengo de Python, donde puedes hacer tuple(list(1,2,3))
.
¿Es corto del lenguaje Scala? (La respuesta es: no se trata de Scala o Python, se trata de tipo estático y de tipo dinámico).
Eso me hace tratar de encontrar el quid por qué Scala no puede hacer esto.
El siguiente ejemplo de código implementa un método toTuple
, que tiene type-safe toTupleN
y type-unsafe toTuple
.
El método toTuple
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 devolución es Product
que es muy similar a la tuple
de Python (no hay tipo en cada posición ni longitud de tipos).
De esa forma se pronedó el error de tiempo de ejecución como type-mismatch o IndexOutOfBoundException
. (por lo que la conveniente lista-a-tupla de Python no es almuerzo gratis).
Por el contrario, es la información de longitud proporcionada por el usuario lo que hace que el toTupleN
compilación toTupleN
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!
Puedes hacerlo usando scala extractors y pattern matching ( link ):
val x = List(1, 2, 3)
val t = x match {
case List(a, b, c) => (a, b, c)
}
Lo cual devuelve una tupla
t: (Int, Int, Int) = (1,2,3)
Además, puede utilizar un operador comodín si no está seguro acerca de un tamaño de la lista
val t = x match {
case List(a, b, c, _*) => (a, b, c)
}
Si está seguro de que su list.size <23 lo usa:
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)
en cuanto tienes el tipo:
val x: List[Int] = List(1, 2, 3)
def doSomething(a:Int *)
doSomething(x:_*)
puedes hacer esto
- a través de la coincidencia de patrones (lo que no desea) o
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)]
un ejemplo usando shapeless :
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 algunos tipos de información para convertir la Lista en la Lista H que la razón por la que necesita pasar información de tipo al método toHList
Shapeless 2.0 cambió algo de sintaxis. Aquí está la solución actualizada que usa información.
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 principal problema es que el tipo para .toHList debe especificarse con anticipación. En términos más generales, dado que las tuplas son limitadas en su aspecto, el diseño de su software podría estar mejor atendido por una solución diferente.
Aún así, si está creando una lista estáticamente, considere una solución como esta, también usando información. 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 puede mapearse entre otras operaciones, como colecciones estándar. Los HLists 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)