List 将Scala列表转换为元组?
如何将包含(比如)3个元素的列表转换为大小为3的元组List 将Scala列表转换为元组?,list,scala,tuples,List,Scala,Tuples,如何将包含(比如)3个元素的列表转换为大小为3的元组 例如,假设我有valx=List(1,2,3),我想把它转换成(1,2,3)。我怎样才能做到这一点?您不能以类型安全的方式来做到这一点。在Scala中,列表是某种类型元素的任意长度序列。就类型系统所知,x可以是任意长度的列表 相反,元组的arity必须在编译时已知。允许将x分配给元组类型将违反类型系统的安全保证 事实上,由于技术原因,Scala元组,但限制在2.11中不再存在,case类限制在2.11中被取消 可以手动编写一个函数,该函数转换
例如,假设我有
valx=List(1,2,3)
,我想把它转换成(1,2,3)
。我怎样才能做到这一点?您不能以类型安全的方式来做到这一点。在Scala中,列表是某种类型元素的任意长度序列。就类型系统所知,x
可以是任意长度的列表
相反,元组的arity必须在编译时已知。允许将x
分配给元组类型将违反类型系统的安全保证
事实上,由于技术原因,Scala元组,但限制在2.11中不再存在,case类限制在2.11中被取消
可以手动编写一个函数,该函数转换多达22个元素的列表,并对较大的列表抛出异常。Scala的模板支持,一个即将推出的特性,将使它更加简洁。但这将是一个丑陋的黑客行为。你不能用类型安全的方式来做这件事。为什么?因为一般来说,我们在运行时之前无法知道列表的长度。但是元组的“长度”必须用它的类型编码,因此在编译时是已知的。例如,
(1,'a',true)
的类型为(Int,Char,Boolean)
,它是元组3[Int,Char,Boolean]
的糖。元组具有此限制的原因是,它们需要能够处理非同构类型。使用以下示例:
注意:编译器需要一些类型信息来转换HList中的列表,这就是为什么需要将类型信息传递给
toHList
方法的原因,尽管简单且不适用于任何长度的列表,但它是类型安全的,在大多数情况下,答案是:
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 x: List[Int] = List(1, 2, 3)
def doSomething(a:Int *)
doSomething(x:_*)
更改了一些语法。下面是使用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
主要问题是必须提前指定.toHList的类型。更一般地说,由于元组在算术上是有限的,所以不同的解决方案可能更好地服务于软件的设计
但是,如果你静态地创建一个列表,那么考虑一个像这样的解决方案,也使用无形状的。在这里,我们直接创建一个HList,该类型在编译时可用。请记住,HList同时具有列表和元组类型的特性。i、 它可以有不同类型的元素,比如元组,并且可以映射到其他操作,比如标准集合。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)
您可以使用scala提取器和模式匹配()完成此操作: 返回一个元组
t: (Int, Int, Int) = (1,2,3)
此外,如果不确定列表的大小,可以使用通配符运算符
val t = x match {
case List(a, b, c, _*) => (a, b, c)
}
FWIW,我想要一个元组初始化许多字段,并且想要使用元组赋值的语法糖。 例如: 事实证明,分配列表的内容也有语法上的糖分
val c1 :: c2 :: c3 :: Nil = myList
因此,如果你有同样的问题,就不需要元组了。2015年邮报。
为了更清楚地说明问题,这里有一个真实的例子
起初,我对此感到困惑。因为我来自Python,在那里你可以只做tuple(list(1,2,3))
它缺少Scala语言吗?(答案是——这与Scala或Python无关,而是关于静态类型和动态类型。) 这导致我试图找到Scala无法做到这一点的症结所在
下面的代码示例实现了一个
toTuple
方法,该方法具有类型safetoTuple
和类型safetoTuple
toTuple
方法在运行时获取类型长度信息,即在编译时没有类型长度信息,因此返回类型是Product
,这与Python的tuple
非常相似(每个位置都没有类型,也没有类型长度)。这样会导致运行时错误,如类型不匹配或
IndexOutOfBoundException
。(因此Python方便的tuple列表不是免费的午餐。)
相反,正是用户提供的长度信息使得toTupleN
编译时安全
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!
你也可以这样做
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)]
如果您非常确定您的list.size也可以在
无形状
中使用大小
以较少的样板文件完成此操作:
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
(可以转换为无形状.Nat
)。使用模式匹配:
val intTuple = List(1,2,3) match {case List(a, b, c) => (a, b, c)}
这是不可能的(除非“用手”)。给定代码< > DEF totuple(x:List[t]):r的类型是什么?如果它不需要对任意大小的元组进行处理(如上面提出的假设方法),请考虑<代码> x匹配{实例A::B::C::NIL= >(a,b,c);Case*=>(0, 0, 0)}< /代码>,并注意结果类型固定在<代码> tup3[int,int,Int ]。虽然使用
List
无法实现您想要做的事情,但您可以查看shapess'HList
类型,它允许元组之间的转换()。也许它适用于您的用例。在这个解决方案的上下文中,对类型安全性的抱怨意味着您可能在运行时遇到匹配错误。如果您愿意,您可以尝试捕获该错误,并在列表长度错误时执行某些操作。这个解决方案的另一个很好的特性是,输出不必是元组,它可以是您自己的自定义类或数字的某种转换。不过,我不认为可以对非列表执行此操作(因此可能需要对inp执行.toList操作)
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!
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)]
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)
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))
val intTuple = List(1,2,3) match {case List(a, b, c) => (a, b, c)}