转换一个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:_*)
你也可以做到这一点
- 通过模式匹配(你不想要的)或者
-
通过迭代列表并逐一应用每个元素。
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
)。