将Scala列表转换为元组?

gra*_*tur 81 scala tuples list

如何将带有(例如)3个元素的列表转换为3个元组?

例如,假设我有val x = List(1, 2, 3),我想将其转换为(1, 2, 3).我怎样才能做到这一点?

cyr*_*llk 52

你可以使用scala提取器和模式匹配(链接)来做到这一点:

val x = List(1, 2, 3)

val t = x match {
  case List(a, b, c) => (a, b, c)
}
Run Code Online (Sandbox Code Playgroud)

哪个返回一个元组

t: (Int, Int, Int) = (1,2,3)
Run Code Online (Sandbox Code Playgroud)

此外,如果不确定List的大小,则可以使用通配符运算符

val t = x match {
  case List(a, b, c, _*) => (a, b, c)
}
Run Code Online (Sandbox Code Playgroud)

  • 在此解决方案的上下文中,关于类型安全性的抱怨意味着您可能在运行时获得MatchError.如果您愿意,可以尝试捕获该错误,并在List具有错误的长度时执行某些操作.此解决方案的另一个不错的功能是输出不必是元组,它可以是您自己的自定义类之一或数字的某些转换.我不认为你可以使用非列表执行此操作(因此您可能需要对输入执行.toList操作). (4认同)

Tom*_*ett 51

你不能以类型安全的方式做到这一点.为什么?因为通常我们无法知道列表的长度,直到运行时.但是元组的"长度"必须以其类型编码,因此在编译时就已知.例如,(1,'a',true)有类型(Int, Char, Boolean),这是糖Tuple3[Int, Char, Boolean].元组有这种限制的原因是它们需要能够处理非同类型.


小智 46

使用无形的示例:

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
Run Code Online (Sandbox Code Playgroud)

注意:编译器需要一些类型信息来转换HList中的List,这就是你需要将类型信息传递给toHList方法的原因


vir*_*fan 17

Shapeless 2.0改变了一些语法.这是使用无形的更新解决方案.

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
Run Code Online (Sandbox Code Playgroud)

主要问题是必须提前指定.toHList的类型.更一般地说,由于元组在其arity中是有限的,因此可以通过不同的解决方案更好地服务于软件的设计.

尽管如此,如果您要静态创建列表,请考虑像这样的解决方案,也使用无形.在这里,我们直接创建一个HList,并且该类型在编译时可用.请记住,HList具有List和Tuple类型的功能.即它可以具有不同类型的元素,如元组,并且可以在标准集合等其他操作中进行映射.HLists需要一点时间来适应,但如果你是新手,那么就慢慢走.

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)
Run Code Online (Sandbox Code Playgroud)


小智 10

尽管简单并且不适用于任何长度的列表,但它在大多数情况下都是类型安全的答案:

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))
Run Code Online (Sandbox Code Playgroud)

另一种可能性,当你不想命名列表也不想重复它时(我希望有人可以展示一种方法来避免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
Run Code Online (Sandbox Code Playgroud)


Pet*_*r L 9

FWIW,我想要一个元组来初始化许多领域,并希望使用元组赋值的语法糖.例如:

val (c1, c2, c3) = listToTuple(myList)
Run Code Online (Sandbox Code Playgroud)

事实证明,还有用于分配列表内容的语法糖...

val c1 :: c2 :: c3 :: Nil = myList
Run Code Online (Sandbox Code Playgroud)

因此,如果你遇到同样的问题,就不需要元组.


Mec*_*ail 6

你不能以类型安全的方式做到这一点.在Scala中,列表是某种类型的元素的任意长度序列.就类型系统所知,x可以是任意长度的列表.

相反,元组的arity必须在编译时知道.它会违反类型系统的安全保证,以允许分配x元组类型.

事实上,由于技术原因,Scala元组仅限于22个元素,但2.11中的限制不再存在案例类限制已在2.11中解除https://github.com/scala/scala/pull/2305

可以手动编写转换最多22个元素的列表的函数,并为较大的列表引发异常.Scala的模板支持,即将推出的功能,将使这更加简洁.但这将是一个丑陋的黑客.


小智 6

使用模式匹配:

val intTuple = List(1,2,3) match {case List(a, b, c) => (a, b, c)}
Run Code Online (Sandbox Code Playgroud)


Spa*_*ner 5

如果您非常确定您的 list.size<23 使用它:

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)
Run Code Online (Sandbox Code Playgroud)


Kol*_*mar 5

这也可以shapeless使用更少的样板来完成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))
Run Code Online (Sandbox Code Playgroud)

它是类型安全的:None如果元组大小不正确,则会得到,但元组大小必须是文字或final val(可转换为shapeless.Nat)。