Scala:一次通过映射和解压缩列表

时间:2018-05-03 01:34:18

标签: scala list dictionary collections tuples

我有一个元组列表如下:

[(8, 3, 9), (10, 3, 0), (-37, 4, 1)]

我想映射此列表并同时将其解压缩一次。这是一个有两遍的例子(或者至少我强烈认为它需要两遍,如果没有,那么我们在这里完成:D)

val l = List((8, 3, 9), (10, 3, 0), (-37, 4, 1))
val (list1, list2) = l.map({ el => (el._1, el._2) }).unzip

当然,我可以通过循环遍历列表并附加到集合来以命令式方式执行此操作但是有没有办法以简洁的功能方式执行此操作?我猜我基本上需要一个懒惰的map,然后是一个急切的unzip

3 个答案:

答案 0 :(得分:3)

这非常特定于您陈述的问题;它没有解决更普遍的问题。尝试:

val (list1, list2, _) = l.unzip3

修改

公平地说,在找到实现scala.collection.generic.GenericTraversableTemplate.unzip3之后,它是一个非常无功能的循环,它构建了3个列表并返回它们,就像原始问题中所描述的那样。至少这是一次传球而不仅仅是掩埋两次传球。

答案 1 :(得分:2)

总是fold

val (list1, list2) = l.foldRight((List.empty[Int],List.empty[Int])){
  case ((a,b,_),(l1,l2)) => (a::l1,b::l2)
}

答案 2 :(得分:0)

unzip与函数配合使用

在Scala中,unzip操作使用一个函数作为隐式参数来生成该对,因此您可以一次性使用mapunzipListScala 2.13.4 docs中的外观如下:

def unzip[A1, A2](implicit asPair: (A) => (A1, A2)): (List[A1], List[A2])

通过传递自己的asPair函数,您可以完成所需的结果:

scala> val l = List((8, 3, 9), (10, 3, 0), (-37, 4, 1))
val l: List[(Int, Int, Int)] = List((8,3,9), (10,3,0), (-37,4,1))

scala> val (list1, list2) = l.unzip(el => (el._1, el._2))
val list1: List[Int] = List(8, 10, -37)
val list2: List[Int] = List(3, 3, 4)

算法复杂度

unzip函数的源代码位于文件Iterable.scala中的特征scala.collection.IterableOps中:

def unzip[A1, A2](implicit asPair: A => (A1, A2)): (CC[A1], CC[A2]) = {
  val first: View[A1] = new View.Map[A, A1](this, asPair(_)._1)
  val second: View[A2] = new View.Map[A, A2](this, asPair(_)._2)
  (iterableFactory.from(first), iterableFactory.from(second))
}

如果我没看错,该列表实际上会遍历两次:每个生成的列表一次。

但是,在您的原始解决方案中,该列表遍历了三遍:一次用于map操作,两次遍历unzip操作,因此我的解决方案仍然是一个改进。

@jwvh建议的使用foldRight的解决方案似乎只遍历该列表一次,但是foldRight的开销是最初反转该列表(以便从列表的开头获取)。所以我想我的解决方案具有完全相同的算法复杂度。 (如果我错了,请纠正我。)