将Scala列表转换为元组?

时间:2013-02-06 06:23:18

标签: list scala tuples

如何将带有(比方说)3个元素的列表转换为大小为3的元组?

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

13 个答案:

答案 0 :(得分:45)

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

答案 1 :(得分:44)

您可以使用scala提取器和模式匹配(link):

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)

此外,如果不确定列表的大小

,则可以使用通配符运算符
val t = x match {
  case List(a, b, c, _*) => (a, b, c)
}

答案 2 :(得分:41)

使用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

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

答案 3 :(得分:14)

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

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

但是,如果你是静态创建一个列表,请考虑像这样的解决方案,也使用无形。在这里,我们直接创建一个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)

答案 4 :(得分:9)

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

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

答案 5 :(得分:9)

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

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

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

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

如果您遇到同样的问题,那么就不需要元组。

答案 6 :(得分:6)

您不能以类型安全的方式执行此操作。在Scala中,列表是某种类型元素的任意长度序列。就类型系统所知,x可以是任意长度的列表。

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

事实上,由于技术原因,Scala元组were limited to 22 elements,但2.11中的限制不再存在案例类限制已在2.11中提升https://github.com/scala/scala/pull/2305

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

答案 7 :(得分:3)

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

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

答案 8 :(得分:2)

如果您确定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)

答案 9 :(得分:1)

只要你有类型:

val x: List[Int] = List(1, 2, 3)

def doSomething(a:Int *)

doSomething(x:_*)

答案 10 :(得分:1)

2015年帖子。 要让Tom Crockett's answer更多澄清,这是一个真实的例子。

起初,我对此感到困惑。因为我来自Python,你可以在那里tuple(list(1,2,3)) 它缺少Scala语言吗? (答案是 - 它不是关于Scala或Python,而是关于静态类型和动态类型。)

这导致我试图找到解释为什么Scala无法做到这一点。


以下代码示例实现了toTuple方法,该方法具有类型安全toTupleN和类型不安全toTuple

toTuple方法在运行时获取类型长度信息,即在编译时没有类型长度信息,因此返回类型为Product,这与Python的{{1}非常相似1}}确实(每个位置没有类型,没有类型的长度) 这种方式与类型不匹配或tuple等运行时错误有关。 (所以Python方便的列表到元组不是免费的午餐。)

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

toTupleN

答案 11 :(得分:1)

你可以这样做

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

答案 12 :(得分:1)

使用模式匹配:

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