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)
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)
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)
因此,如果你遇到同样的问题,就不需要元组.
你不能以类型安全的方式做到这一点.在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)
如果您非常确定您的 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)
这也可以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)。