我有一个元组列表如下:
[(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
。
答案 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
操作使用一个函数作为隐式参数来生成该对,因此您可以一次性使用map
和unzip
。 List
在Scala 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
的开销是最初反转该列表(以便从列表的开头获取)。所以我想我的解决方案具有完全相同的算法复杂度。 (如果我错了,请纠正我。)