我可以在 Scala 中将两个以上的列表压缩在一起吗?
Posted
技术标签:
【中文标题】我可以在 Scala 中将两个以上的列表压缩在一起吗?【英文标题】:Can I zip more than two lists together in Scala? 【发布时间】:2010-12-12 11:24:07 【问题描述】:给定以下 Scala 列表:
val l = List(List("a1", "b1", "c1"), List("a2", "b2", "c2"), List("a3", "b3", "c3"))
如何获得:
List(("a1", "a2", "a3"), ("b1", "b2", "b3"), ("c1", "c2", "c3"))
由于 zip 只能用于组合两个列表,我认为您需要以某种方式迭代/减少主列表。毫不奇怪,以下方法不起作用:
scala> l reduceLeft ((a, b) => a zip b)
<console>:6: error: type mismatch;
found : List[(String, String)]
required: List[String]
l reduceLeft ((a, b) => a zip b)
有什么建议吗?我想我错过了一种非常简单的方法。
更新:我正在寻找一种解决方案,它可以获取一个包含 N 个列表的列表,每个列表包含 M 个元素,并创建一个包含 M 个 TupleN 的列表。
更新 2: 事实证明,对于我的特定用例来说,最好有一个列表列表,而不是一个元组列表,所以我接受了南瓜的回应。它也是最简单的,因为它使用本地方法。
【问题讨论】:
Zip multiple sequences的可能重复 绝对值得注意:***.com/questions/1683312/… @VenkatSudheerReddyAedama 五天后我也问了。 ;-) 【参考方案1】:scala> (List(1,2,3),List(4,5,6),List(7,8,9)).zipped.toList
res0: List[(Int, Int, Int)] = List((1,4,7), (2,5,8), (3,6,9))
供日后参考。
【讨论】:
这非常适合压缩三个列表。可惜这不适用于三个以上的列表:( 请注意,这需要首先在一个元组中:zipped
不是List
的函数。
zipped
在 Scala 2.13 中已弃用。在 2.13 中,做l1.lazyZip(l2).lazyZip(l3).toList
【参考方案2】:
我不相信生成任意大小的元组列表是可能的,但是如果您不介意获取列表列表,transpose function 完全可以满足您的需求。
【讨论】:
谢谢,完美!当我进入我的具体用例时,我发现列表列表会更好,因为我需要映射和减少各种子列表。 @JoshCason 最狭义的“超过两个”,当然。三确实大于二。我从广义上的“超过两个”来解释这个问题,意思是任意多个。在这种情况下,除非你达到HList
s 之类的要求,否则不可能按照问题的要求去做。
答案中的链接已损坏,新链接为scala-lang.org/api/2.12.1/scala/…【参考方案3】:
所以这段代码不能满足 OP 的需求,不仅因为这是一个已有四年历史的线程,而且它确实回答了标题问题,也许有人甚至会觉得它有用。
压缩 3 个集合:
as zip bs zip cs map
case ((a,b), c) => (a,b,c)
【讨论】:
做4个收藏看起来像:as zip bs zip cs zip ds map case ((a,b),c) map case ((a,b),c,d)=>(a,b,c,d)
@JamesTobin,你缩短为as zip bs zip cs zip ds map case (((a,b),c),d)=>(a,b,c,d)
很适合各种类型的列表。【参考方案4】:
是的,zip3。
【讨论】:
谢谢,但它只适用于 3 个列表。我正在寻找一种解决方案,它可以获取一个包含 N 个列表的列表,每个列表包含 M 个元素并创建一个 M 个 TupleN 的列表。【参考方案5】:transpose
可以解决问题。一种可能的算法是:
def combineLists[A](ss:List[A]*) =
val sa = ss.reverse;
(sa.head.map(List(_)) /: sa.tail)(_.zip(_).map(p=>p._2 :: p._1))
例如:
combineLists(List(1, 2, 3), List(10,20), List(100, 200, 300))
// => List[List[Int]] = List(List(1, 10, 100), List(2, 20, 200))
答案被截断为输入中最短列表的大小。
combineLists(List(1, 2, 3), List(10,20))
// => List[List[Int]] = List(List(1, 10), List(2, 20))
【讨论】:
这个答案几乎可以解决问题,但是,它颠倒了元素。你能推荐一个按预期顺序产生输出的改进版本吗?谢谢 保留顺序的修改版:def combineLists[A](ss:List[A]*) = val sa = ss.reverse; (sa.head.map(List(_)) /: sa.tail)(_.zip(_).map(p=>p._2 :: p._1))
【参考方案6】:
Scala 将所有不同的元组大小视为不同的类(Tuple1
、Tuple2
、Tuple3
、Tuple4
、...、Tuple22
),而它们都继承自 Product
特征,如果它们都可以由同一个函数返回,那么该特征没有携带足够的信息来实际使用来自不同大小的元组的数据值。 (而且 scala 的泛型也不足以处理这种情况。)
最好的办法是为所有 22 个元组大小编写 zip 函数的重载。代码生成器可能会帮助您。
【讨论】:
【参考方案7】:如果你不想走 applicative scalaz/cats/(在此处插入你最喜欢的函数库) 路线,模式匹配是要走的路,虽然 (_, _)
语法在嵌套时有点尴尬,所以让我们改变它:
import scala.Tuple2 => &
for (i1 & i2 & i3 & i4 <- list1 zip list2 zip list3 zip list4) yield (i1, i2, i3, i4)
&
在这里是任意选择,任何看起来不错的中缀都应该这样做。不过,在代码审查期间,您可能会受到一些质疑。
它也应该适用于任何你可以zip
(例如Future
s)
【讨论】:
【参考方案8】:如果不重复,我认为这是不可能的。一个简单的原因:你不能定义你要求的函数的返回类型。
例如,如果您的输入是List(List(1,2), List(3,4))
,那么返回类型将是List[Tuple2[Int]]
。如果它有三个元素,则返回类型将为List[Tuple3[Int]]
,依此类推。
你可以返回List[AnyRef]
,甚至是List[Product]
,然后制作一堆case,每个条件一个。
至于一般的 List 转置,这是可行的:
def transpose[T](l: List[List[T]]): List[List[T]] = l match
case Nil => Nil
case Nil :: _ => Nil
case _ => (l map (_.head)) :: transpose(l map (_.tail))
【讨论】:
这不适用于任意大小的列表。例如:transpose(List(List("a", "b"), List("c"))) @VenkatSudheerReddyAedama 不完整矩阵的转置对我来说没有意义。举个例子,如果c
与a
一致,还是与b
一致?你如何表示它与另一个一致?
同意。这是一个不完整的矩阵。我一直在寻找类似 zipAll 的东西。就我而言,c
与 a
一致(即与索引一致)?【参考方案9】:
product-collections 有一个flatZip
操作,直到 arity 22。
scala> List(1,2,3) flatZip Seq("a","b","c") flatZip Vector(1.0,2.0,3.0) flatZip Seq(9,8,7)
res1: com.github.marklister.collections.immutable.CollSeq4[Int,String,Double,Int] =
CollSeq((1,a,1.0,9),
(2,b,2.0,8),
(3,c,3.0,7))
【讨论】:
【参考方案10】:Scala 2.12.13 及更低版本
如果知道输入的List有多长,可以将列表加入一个Tuple,使用Tuple的.zipped
方法:
val l = List(List("a1", "b1", "c1"), List("a2", "b2", "c2"), List("a3", "b3", "c3"))
println(l match
case l1::l2::l3::_ => (l1,l2,l3).zipped.toList
case _ => throw new IllegalArgumentException("List is not the right length")
) // List((a1,a2,a3), (b1,b2,b3), (c1,c2,c3))
Scastie Example - 2.12.13
>= 斯卡拉 2.13
不推荐使用上述解决方案 - 改用lazyZip:
val l = List(List("a1", "b1", "c1"), List("a2", "b2", "c2"), List("a3", "b3", "c3"))
println(l match
case l1::l2::l3::_ => (l1 lazyZip l2 lazyZip l3).toList
case _ => throw new IllegalArgumentException("List is not the right length")
) // List((a1,a2,a3), (b1,b2,b3), (c1,c2,c3))
Scastie Example - 2.13.0
【讨论】:
【参考方案11】:使用 Scalaz:
import scalaz.Zip
import scalaz.std.list._
// Zip 3
Zip[List].ap.tuple3(List("a1", "b1"),
List("a2", "b2"),
List("a3", "b3"))
// Zip 4
Zip[List].ap.tuple4(List("a1", "b1"),
List("a2", "b2"),
List("a3", "b3"),
List("a4", "b4"))
// Zip 5
Zip[List].ap.tuple5(List("a1", "b1"),
List("a2", "b2"),
List("a3", "b3"),
List("a4", "b4"),
List("a5", "b5"))
5 人以上:
// Zip 6
Zip[List].ap.apply6(List("a1", "b1"),
List("a2", "b2"),
List("a3", "b3"),
List("a4", "b4"),
List("a5", "b5"),
List("a6", "b6"))((_, _, _, _, _, _))
// Zip 7
Zip[List].ap.apply7(List("a1", "b1"),
List("a2", "b2"),
List("a3", "b3"),
List("a4", "b4"),
List("a5", "b5"),
List("a6", "b6"),
List("a7", "b7"))((_, _, _, _, _, _, _))
...
// Zip 12
Zip[List].ap.apply12(List("a1", "b1"),
List("a2", "b2"),
List("a3", "b3"),
List("a4", "b4"),
List("a5", "b5"),
List("a6", "b6"),
List("a7", "b7"),
List("a8", "b8"),
List("a9", "b9"),
List("a10", "b10"),
List("a11", "b11"),
List("a12", "b12"))((_, _, _, _, _, _, _, _, _, _, _, _))
【讨论】:
以上是关于我可以在 Scala 中将两个以上的列表压缩在一起吗?的主要内容,如果未能解决你的问题,请参考以下文章