【问题标题】:Zip multiple sequences压缩多个序列
【发布时间】:2012-03-26 18:54:47
【问题描述】:

我正在尝试将zip多个序列组成一个长元组:

val ints = List(1,2,3)
val chars = List('a', 'b', 'c')
val strings = List("Alpha", "Beta", "Gamma")
val bools = List(true, false, false)

ints zip chars zip strings zip bools

我得到了什么:

List[(((Int, Char), String), Boolean)] =
  List((((1,a),Alpha),true), (((2,b),Beta),false), (((3,c),Gamma),false))

但是我想得到一系列 flat 元组:

List[(Int, Char, String, Boolean)] = 
  List((1,a,Alpha,true), (2,b,Beta,false), (3,c,Gamma,false))

我现在可以做到:

List(ints, chars, strings, bools).transpose

但它返回弱类型List[List[Any]]。我也可以(ints, chars, strings).zipped,但zipped 仅适用于 2 元组和 3 元组。

有没有办法轻松压缩(任意)数量的等长序列?

【问题讨论】:

    标签: scala tuples


    【解决方案1】:

    这是解决示例的一种方法,但这不适用于任意数量的序列。

    val ints = List(1,2,3)
    val chars = List('a', 'b', 'c')
    val strings = List("Alpha", "Beta", "Gamma")
    val bools = List(true, false, false)
    
    val input = ints zip chars zip strings zip bools
    
    // Flattens a tuple ((A,B),C) into (A,B,C)
    def f2[A,B,C](t: ((A,B),C)) = (t._1._1, t._1._2, t._2)
    
    // Flattens a tuple ((A,B),C,D) into (A,B,C,D)
    def f3[A,B,C,D](t: ((A,B),C,D)) = (t._1._1, t._1._2, t._2, t._3)
    
    input map f2 map f3
    

    我认为对于任意长度的元组一般来说是不可能的,至少在这种解决方案中是不可能的。元组是强类型的,据我所知,类型系统不允许您指定可变数量的类型参数,这使得不可能制作一个通用版本的 f2f3任意长度的元组((A,B),C,D,...)(这将返回一个元组(A,B,C,D,...))。

    如果有一种方法可以指定可变数量的类型参数,我们就不需要 Scala 标准库中的特征 Tuple1Tuple2、...Tuple22

    【讨论】:

    • +1,谢谢。我目前正在使用map 方法,但t._1._1, t._1._2, t._2, t._3 的可读性不是很好——在我的情况下,我需要一个 5 元组,这让事情变得更糟。我真的不需要支持任意长度的列表,但足够长。我虽然可能有一些专门的方法可以返回正确长度的强类型元组,但我明白你关于Tuple1-Tuple22 问题的观点。
    • 通过模式匹配,您可以摆脱不可读的._1, ._2 等语法:def f2[A,B,C](t: ((A,B),C)) = t match { case ((a, b), c) => (a, b, c) }
    【解决方案2】:

    我同意 Jesper 的观点,即这通常是不可能的,因为每个元组在源代码中都表示为单独的类,因此除非使用代码生成器,否则您必须编写单独的代码来访问它们。

    但我想添加另一个可能的解决方案。如果您想保留元组条目的类型,但又对更像集合的类型感兴趣,那么 HLists(异构列表)可能适合您。您可以google hlist scala获取实现和解释。

    【讨论】:

      【解决方案3】:

      使用shapeless,您可以:

      import shapeless.Tuples._
      
      val ints = (1, 2, 3)
      val chars = ('a', 'b', 'c')
      
      val megatuple = (ints, chars)
      
      val megahlist = (megatuple hlisted) map hlisted
      
      val transposed = (mhlist transpose) map tupled tupled
      
      scala> transposed
      res: ((Int, Char), (Int, Char), (Int, Char)) = ((1,a),(2,b),(3,c))
      

      (不确定,是否定义了更多隐含可以避免map 和来回转换)

      [编辑:这部分不再正确。

      请注意,无形文档说,目前仅支持高达 Tuple4 的转换。然后你必须手动创建 HLists。]

      【讨论】:

      • 实际上无形处理最多 22 的所有 arities。
      • 啊,好的。我在conversions.scala中的评论没有实际检查它。
      • 哎呀...过时的评论。现在修好了。感谢您的提醒。
      【解决方案4】:

      我会创建一个代表数据集的类:

      case class DataSet(int: Int, char: Char, string: String, bool: Boolean)
      

      这为访问值带来了更好的名称,而不是我们在元组中的_N。如果列表可以有不同的大小,则应选择最短的:

      val min = List(ints, chars, strings, bools).map(_.size).min
      

      现在可以提取数据了:

      val dataSets = (0 until min) map { i => DataSet(ints(i), chars(i), strings(i), bools(i)) }
      

      当原始列表可以包含很多值时,最好将它们设置为IndexedSeq,以便访问时间为 O(1)。

      【讨论】:

        【解决方案5】:

        使用product-collections

        scala> ints flatZip chars flatZip strings flatZip bools
        res0: org.catch22.collections.immutable.CollSeq4[Int,Char,String,Boolean] = 
        CollSeq((1,a,Alpha,true),
                (2,b,Beta,false),
                (3,c,Gamma,false))
        

        这目前适用于 arity 1 - 22。如您所见,类型被保留。

        【讨论】:

          【解决方案6】:

          我认为模式匹配是一个不错的选择

          val ints = List(1,2,3)
          val chars = List('a', 'b', 'c')
          val strings = List("Alpha", "Beta", "Gamma")
          val bools = List(true, false, false)
          (ints zip chars zip strings zip bools) map { case (((i,c),s),b) => (i,c,s,b)}
          
          **res1: List[(Int, Char, java.lang.String, Boolean)] = List((1,a,Alpha,true), (2,b,Beta,false), (3,c,Gamma,false))**
          

          或者你也可以添加类型

          (ints zip chars zip strings zip bools) map {case (((i:Int,c:Char),s:String),b:Boolean) => (i,c,s,b)}
          
          **res2: List[(Int, Char, java.lang.String, Boolean)] = List((1,a,Alpha,true), (2,b,Beta,false), (3,c,Gamma,false))**
          

          【讨论】:

            【解决方案7】:

            这是另一个可以解决您的问题的解决方案。

            ints zip chars zip strings zip bools map{ case (((a, b), c), d) => (a,b,c,d)}
            

            【讨论】:

              【解决方案8】:

              为什么不使用zip 而不是For Comprehension

              val ints = List(1,2,3)
              val chars = List('a', 'b', 'c')
              val strings = List("Alpha", "Beta", "Gamma")
              val bools = List(true, false, false)
              
              val tuples: List[(Int, Char, String, Boolean)] = for {
                int <- ints
                char <- chars
                string <- strings
                bool <- bools
              } yield (int, char, string, bool)
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2013-05-06
                • 2018-01-04
                • 2011-01-28
                • 1970-01-01
                • 1970-01-01
                • 2014-06-07
                相关资源
                最近更新 更多