转换一个Scala列表到一个元组?

我怎样才能将(说)3个元素的列表转换成3的元组?

例如,假设我有val x = List(1, 2, 3) ,我想将其转换为(1, 2, 3) 。 我该怎么做?

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

一个使用无形的例子:

 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 

注意:编译器需要一些types信息来将HList中的List转换为你需要将types信息传递给toHList方法的原因

你可以使用scala提取器和模式匹配( 链接 ):

 val x = List(1, 2, 3) val t = x match { case List(a, b, c) => (a, b, c) } 

哪个返回一个元组

 t: (Int, Int, Int) = (1,2,3) 

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

 val t = x match { case List(a, b, c, _*) => (a, b, c) } 

无形2.0改变了一些语法。 这里是使用无形的更新解决scheme。

 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 

主要的问题是.toHList的types必须提前指定。 更一般地说,由于元组的有限性,软件的devise可能会更好地由不同的解决scheme提供服务。

不过,如果你正在静态地创build一个列表,考虑一个这样的解决scheme,也使用无形。 在这里,我们直接创build一个HList,并且这个types在编译时可用。 请记住,HList具有来自List和Tupletypes的特征。 即它可以具有不同types的元素(如Tuple),并可以映射到其他操作(如标准集合)之间。 如果你是新的,HList需要一点时间才能习惯。

 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) 

尽pipe简单,而不是任何长度的列表,它是types安全的,在大多数情况下的答案:

 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)) 

另一种可能性,当你不想列出名单或重复它(我希望有人可以显示一种方法来避免Seq /头部分):

 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 

FWIW,我想要一个元组来初始化一些字段,并希望使用元组赋值的语法糖。 例如:

 val (c1, c2, c3) = listToTuple(myList) 

事实certificate,也有语法糖分配列表的内容…

 val c1 :: c2 :: c3 :: Nil = myList 

所以如果你遇到同样的问题,就不需要元组了。

你不能以types安全的方式做到这一点。 在Scala中,列表是一些types元素的任意长度序列。 就types系统而言, x可以是任意长度的列表。

相反,元组的元组必须在编译时才知道。 这将违反types系统的安全保证,允许将x分配给元组types。

实际上,由于技术上的原因,斯卡拉元组被限制在22个元素 ,但是这个限制在2.11中已经不复存在了2.11个案例类的限制已经解除https://github.com/scala/scala/pull/2305

可以手动编写一个函数来转换多达22个元素的列表,并为较大的列表抛出exception。 Scala的模板支持,即将到来的function,将使这更简洁。 但是这将是一个丑陋的黑客。

至于你有这样的types:

 val x: List[Int] = List(1, 2, 3) def doSomething(a:Int *) doSomething(x:_*) 

你也可以做到这一点

  1. 通过模式匹配(你不想要的)或者
  2. 通过迭代列表并逐一应用每个元素。

     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)] 

2015年的职位。 对于Tom Crockett的回答更加清晰 ,这里就是一个真实的例子。

起初,我对此感到困惑。 因为我来自Python,在那里你可以做tuple(list(1,2,3))
它是否缺lessScala语言? (答案是 – 这不是关于Scala或Python,而是关于静态types和dynamictypes的。)

这导致我试图find为什么斯卡拉不能做到这一点的关键。


下面的代码示例实现了toTuple方法,该方法对types安全的toTupleN和types不安全的toTuple

toTuple方法在运行时获取types长度信息,即在编译时没有types长度的信息,所以返回types是Product ,它非常类似于Python的tuple (每个位置都没有types,没有长度types)。
这种方式是运行时错误,如types不匹配或IndexOutOfBoundException 。 (所以Python的list-to-tuple不是免费的午餐。)

相反,用户提供的长度信息使得编译时安全。

 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! 

如果你很确定你的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) 

这也可以在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)) 

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