Não é possível (exceto "à mão") AFAIK. Dado def toTuple(x: List[Int]): R, qual deve ser o tipo de R?
7
Se não precisar ser feito para uma tupla de tamanho arbitrário (ou seja, como um método hipotético apresentado acima), considere x match { case a :: b :: c :: Nil => (a, b, c); case _ => (0, 0, 0) }e observe que o tipo resultante é fixado emTuple3[Int,Int,Int]
2
Embora o que você procura fazer não seja possível List, você pode olhar para o HListtipo Shapeless , que permite a conversão de e para tuplas ( github.com/milessabin/… ). Talvez seja aplicável ao seu caso de uso.
Respostas:
59
Você não pode fazer isso de uma forma segura. Por quê? Porque, em geral, não podemos saber o comprimento de uma lista até o tempo de execução. Mas o "comprimento" de uma tupla deve ser codificado em seu tipo e, portanto, conhecido em tempo de compilação. Por exemplo, (1,'a',true)tem o tipo (Int, Char, Boolean), que é açúcar para Tuple3[Int, Char, Boolean]. A razão pela qual as tuplas têm essa restrição é que elas precisam ser capazes de lidar com tipos não homogêneos.
Existe alguma lista de tamanho fixo de elementos com o mesmo tipo? Não importando bibliotecas fora do padrão, como o shapeless, é claro.
1
@davips não que eu saiba, mas é fácil o suficiente para fazer o seu próprio, por exemplocase class Vec3[A](_1: A, _2: A, _3: A)
Tom Crockett
Isso seria uma "tupla" de elementos do mesmo tipo, não posso aplicar map nela, por exemplo.
1
@davips como acontece com qualquer novo tipo de dados, você terá que definir como mapetc. funciona para ele
Tom Crockett
1
Esse tipo de limitação parece ser um indicador retumbante da inutilidade do tipo de segurança, mais do que qualquer outra coisa. Isso me lembra a citação "o conjunto vazio tem muitas propriedades interessantes."
ely
58
Você pode fazer isso usando extratores de scala e correspondência de padrões ( link ):
val x = List(1, 2, 3)
val t = x match {
caseList(a, b, c) => (a, b, c)
}
Que retorna uma tupla
t: (Int, Int, Int) = (1,2,3)
Além disso, você pode usar um operador curinga se não tiver certeza sobre o tamanho da lista
val t = x match {
caseList(a, b, c, _*) => (a, b, c)
}
No contexto desta solução, as reclamações sobre segurança de tipo significam que você pode obter um MatchError em tempo de execução. Se quiser, você pode tentar detectar esse erro e fazer algo se a lista tiver o comprimento errado. Outro recurso interessante dessa solução é que a saída não precisa ser uma tupla, pode ser uma de suas próprias classes personalizadas ou alguma transformação dos números. Eu não acho que você pode fazer isso com não-listas, embora (então você pode precisar fazer uma operação .toList para a entrada).
Jim Pivarski
Você pode fazer isso com qualquer tipo de sequência Scala usando a sintaxe +:. Além disso, não se esqueça de adicionar um pega-tudo
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: o compilador precisa de algumas informações de tipo para converter a lista na HList que é o motivo pelo qual você precisa passar informações de tipo para o toHListmétodo
Shapeless 2.0 mudou algumas sintaxes. Aqui está a solução atualizada usando o 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
O principal problema é que o tipo de .toHList deve ser especificado com antecedência. De forma mais geral, como as tuplas são limitadas em sua aridade, o design do seu software pode ser melhor servido por uma solução diferente.
Ainda assim, se você estiver criando uma lista estaticamente, considere uma solução como esta, também usando shapeless. Aqui, criamos uma HList diretamente e o tipo está disponível em tempo de compilação. Lembre-se de que uma HList possui recursos dos tipos Lista e Tupla. ou seja, pode ter elementos com diferentes tipos, como uma tupla, e pode ser mapeado entre outras operações, como coleções padrão. Listas demoram um pouco para se acostumar, então vá devagar se você for novo.
@javadba Eu estava procurando como implementar listToTuple () mas acabei não precisando. A lista sintática de açúcar resolveu meu problema.
Peter L
Há também outra sintaxe, que na minha opinião parece um pouco melhor:val List(c1, c2, c3) = myList
Kolmar
7
Você não pode fazer isso de maneira segura. No Scala, as listas são sequências de comprimento arbitrário de elementos de algum tipo. Pelo que o sistema de tipos sabe, xpode ser uma lista de comprimento arbitrário.
Em contraste, a aridade de uma tupla deve ser conhecida em tempo de compilação. Isso violaria as garantias de segurança do sistema de tipos permitir a atribuição xa um tipo de tupla.
Seria possível codificar manualmente uma função que converte listas de até 22 elementos e lança uma exceção para listas maiores. O suporte a modelos do Scala, um recurso futuro, tornaria isso mais conciso. Mas isso seria um hack feio.
É seguro para tipos: você obtém None, se o tamanho da tupla estiver incorreto, mas o tamanho da tupla deve ser literal ou final val(para ser conversível em shapeless.Nat).
Isso não parece funcionar para tamanhos maiores que 22, pelo menos para shapeless_2.11: 2.3.3
kfkhalili
@kfkhalili Sim, e não é uma limitação sem forma. O Scala 2 em si não permite tuplas com mais de 22 elementos, portanto não é possível converter uma lista de tamanho maior em uma tupla usando nenhum método.
Kolmar
4
Usando a correspondência de padrões:
val intTuple = List(1,2,3) match {caseList(a, b, c) => (a, b, c)}
Ao responder uma pergunta tão antiga (mais de 6 anos), é sempre uma boa ideia apontar como sua resposta é diferente de todas as (12) respostas mais antigas já enviadas. Em particular, sua resposta só é aplicável se o comprimento de List/ Tuplefor uma constante conhecida.
jwvh
Obrigado @ jwvh, considerarei seus comentários daqui para frente.
Michael Olafisoye
2
Postagem de 2015. Para que a resposta de Tom Crockett seja mais esclarecedora , aqui está um exemplo real.
No começo, fiquei confuso sobre isso. Porque eu venho de Python, onde você pode simplesmente fazer tuple(list(1,2,3)).
É curto a linguagem Scala? (a resposta é - não é sobre Scala ou Python, é sobre tipo estático e tipo dinâmico.)
Isso me faz tentar encontrar o ponto crucial por que Scala não pode fazer isso.
O exemplo de código a seguir implementa um toTuplemétodo, que possui tipo seguro toTupleNe tipo não seguro toTuple.
O toTuplemétodo obtém as informações de comprimento de tipo em tempo de execução, ou seja, nenhuma informação de comprimento de tipo em tempo de compilação, então o tipo de retorno Producté muito parecido com o do Python tuple(nenhum tipo em cada posição e nenhum comprimento de tipos).
Dessa forma, está sujeito a erros de tempo de execução como incompatibilidade de tipo ou IndexOutOfBoundException. (portanto, a conveniente lista-a-tupla do Python não é um almoço grátis.)
Ao contrário, é a informação de comprimento fornecida pelo usuário que torna o toTupleNtempo de compilação 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
, qual deve ser o tipo de R?x match { case a :: b :: c :: Nil => (a, b, c); case _ => (0, 0, 0) }
e observe que o tipo resultante é fixado emTuple3[Int,Int,Int]
List
, você pode olhar para oHList
tipo Shapeless , que permite a conversão de e para tuplas ( github.com/milessabin/… ). Talvez seja aplicável ao seu caso de uso.Respostas:
Você não pode fazer isso de uma forma segura. Por quê? Porque, em geral, não podemos saber o comprimento de uma lista até o tempo de execução. Mas o "comprimento" de uma tupla deve ser codificado em seu tipo e, portanto, conhecido em tempo de compilação. Por exemplo,
(1,'a',true)
tem o tipo(Int, Char, Boolean)
, que é açúcar paraTuple3[Int, Char, Boolean]
. A razão pela qual as tuplas têm essa restrição é que elas precisam ser capazes de lidar com tipos não homogêneos.fonte
case class Vec3[A](_1: A, _2: A, _3: A)
map
etc. funciona para eleVocê pode fazer isso usando extratores de scala e correspondência de padrões ( link ):
val x = List(1, 2, 3) val t = x match { case List(a, b, c) => (a, b, c) }
Que retorna uma tupla
t: (Int, Int, Int) = (1,2,3)
Além disso, você pode usar um operador curinga se não tiver certeza sobre o tamanho da lista
val t = x match { case List(a, b, c, _*) => (a, b, c) }
fonte
um exemplo usando sem forma :
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: o compilador precisa de algumas informações de tipo para converter a lista na HList que é o motivo pelo qual você precisa passar informações de tipo para o
toHList
métodofonte
Shapeless 2.0 mudou algumas sintaxes. Aqui está a solução atualizada usando o 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
O principal problema é que o tipo de .toHList deve ser especificado com antecedência. De forma mais geral, como as tuplas são limitadas em sua aridade, o design do seu software pode ser melhor servido por uma solução diferente.
Ainda assim, se você estiver criando uma lista estaticamente, considere uma solução como esta, também usando shapeless. Aqui, criamos uma HList diretamente e o tipo está disponível em tempo de compilação. Lembre-se de que uma HList possui recursos dos tipos Lista e Tupla. ou seja, pode ter elementos com diferentes tipos, como uma tupla, e pode ser mapeado entre outras operações, como coleções padrão. Listas demoram um pouco para se acostumar, então vá devagar se você for novo.
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)
fonte
Apesar da simplicidade e de não ser para listas de qualquer tamanho, é seguro para tipos e a resposta na maioria dos 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))
Outra possibilidade, quando você não quiser nomear a lista nem repeti-la (espero que alguém mostre uma forma de evitar as partes 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
fonte
FWIW, eu queria uma tupla para inicializar vários campos e queria usar o açúcar sintático da atribuição de tupla. POR EXEMPLO:
val (c1, c2, c3) = listToTuple(myList)
Acontece que existe açúcar sintático para atribuir o conteúdo de uma lista também ...
val c1 :: c2 :: c3 :: Nil = myList
Portanto, não há necessidade de tuplas se você tiver o mesmo problema.
fonte
val List(c1, c2, c3) = myList
Você não pode fazer isso de maneira segura. No Scala, as listas são sequências de comprimento arbitrário de elementos de algum tipo. Pelo que o sistema de tipos sabe,
x
pode ser uma lista de comprimento arbitrário.Em contraste, a aridade de uma tupla deve ser conhecida em tempo de compilação. Isso violaria as garantias de segurança do sistema de tipos permitir a atribuição
x
a um tipo de tupla.Na verdade, por razões técnicas, as tuplas de Scala eram limitadas a 22 elementos , mas o limite não existe mais em 2.11. O limite de classe de caso foi levantado em 2.11 https://github.com/scala/scala/pull/2305
Seria possível codificar manualmente uma função que converte listas de até 22 elementos e lança uma exceção para listas maiores. O suporte a modelos do Scala, um recurso futuro, tornaria isso mais conciso. Mas isso seria um hack feio.
fonte
Se você tiver certeza de que list.size <23, use-o:
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)
fonte
Isso também pode ser feito
shapeless
com menos clichê 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))
É seguro para tipos: você obtém
None
, se o tamanho da tupla estiver incorreto, mas o tamanho da tupla deve ser literal oufinal val
(para ser conversível emshapeless.Nat
).fonte
Usando a correspondência de padrões:
val intTuple = List(1,2,3) match {case List(a, b, c) => (a, b, c)}
fonte
List
/Tuple
for uma constante conhecida.Postagem de 2015. Para que a resposta de Tom Crockett seja mais esclarecedora , aqui está um exemplo real.
No começo, fiquei confuso sobre isso. Porque eu venho de Python, onde você pode simplesmente fazer
tuple(list(1,2,3))
.É curto a linguagem Scala? (a resposta é - não é sobre Scala ou Python, é sobre tipo estático e tipo dinâmico.)
Isso me faz tentar encontrar o ponto crucial por que Scala não pode fazer isso.
O exemplo de código a seguir implementa um
toTuple
método, que possui tipo segurotoTupleN
e tipo não segurotoTuple
.O
toTuple
método obtém as informações de comprimento de tipo em tempo de execução, ou seja, nenhuma informação de comprimento de tipo em tempo de compilação, então o tipo de retornoProduct
é muito parecido com o do Pythontuple
(nenhum tipo em cada posição e nenhum comprimento de tipos).Dessa forma, está sujeito a erros de tempo de execução como incompatibilidade de tipo ou
IndexOutOfBoundException
. (portanto, a conveniente lista-a-tupla do Python não é um almoço grátis.)Ao contrário, é a informação de comprimento fornecida pelo usuário que torna o
toTupleN
tempo de compilação 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!
fonte
você pode fazer isso também
iterando pela lista e aplicando cada elemento um por um.
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)]
fonte
tanto quanto você tem o tipo:
val x: List[Int] = List(1, 2, 3) def doSomething(a:Int *) doSomething(x:_*)
fonte