List 将Scala列表转换为元组?

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中被取消 可以手动编写一个函数,该函数转换

如何将包含(比如)3个元素的列表转换为大小为3的元组


例如,假设我有
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
方法,该方法具有类型safe
toTuple
和类型safe
toTuple

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