【问题标题】:Efficiently iterate over one Set, and then another, in one for loop in Scala在 Scala 的一个 for 循环中有效地迭代一个 Set,然后另一个 Set
【发布时间】:2013-06-29 08:41:56
【问题描述】:

我想使用单个循环遍历一个Set 的所有元素,然后遍历另一个Set 的所有元素。 (我不关心重复,因为我碰巧知道这两个Sets 是不相交的。)

我想在一个循环中执行它的原因是因为我有一些额外的代码来衡量进度,这要求它在一个循环中。

这通常不起作用,因为它可能会任意混合两个Sets:

for(x <- firstSet ++ secondSet) {
   ...
}

这可行,但在内存中构建了 3 个中间 Seqs,因此在时间和空间使用方面都太低效了:

for(x <- firstSet.toSeq ++ secondSet.toSeq) {
   ...
}

【问题讨论】:

    标签: scala loops scala-collections


    【解决方案1】:
    for(x <- firstSet.toIterator ++ secondSet.toIterator) {
       ...
    }
    

    这不会构建任何中间数据结构,所以我认为这是最有效的方式。

    【讨论】:

    • 我相当肯定,当调用++ 时,这会在内部将两个集合变成一个长的List,所以它不是很有效。不知道为什么@huynhjl 删除了他的答案,但作为参考,他建议使用for (set &lt;- List(firstSet, secondSet); x &lt;- set) { ... } 形式的额外生成器,这似乎是一种有效的方法。
    • @LuigiPlinge,我删除了我的答案,因为我认为我误解了这个问题,因为我的解决方案有一个嵌套循环。我不知道这是否仍然允许“衡量进展”。关于什么是“衡量进度”的问题缺乏细节,我决定删除。
    • @LuigiPlinge 看看++scala.collection.Iterator 中的实现。
    • 该死的,他们什么都想到了!我仍然想不出你为什么不使用额外的生成器。
    【解决方案2】:

    如果你只是想要一个遍历,并且你想要最大的性能,这是最好的方法,即使它很丑:

    val s1 = Set(1,2,3)
    val s2 = Set(4,5,6)
    val block : Int => Unit = x => { println(x) }
    s1.foreach(block)
    s2.foreach(block)
    

    因为这很丑,你可以为它定义一个类:

    def traverse[T](a:Traversable[T], b:Traversable[T]) : Traversable[T] = 
      new Traversable[T] { 
        def foreach[U](f:T=>U) { a.foreach(f); b.foreach(f) } 
      }
    

    然后像这样使用它:

    for(x<-traverse(s1, s2)) println(x)
    

    但是,除非这对性能非常关键,否则 Robin Green 发布的解决方案会更好。开销是创建两个迭代器并将它们连接起来。如果您有更深层次的嵌套数据结构,那么连接迭代器可能会非常昂贵。例如,通过连接子树的迭代器定义的树迭代器将非常缓慢,而您只需在每个子树上调用 foreach 的可遍历树将接近最优。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-02-28
      • 2019-10-04
      • 2015-07-09
      • 2016-10-19
      • 2023-03-17
      • 1970-01-01
      • 1970-01-01
      • 2016-11-20
      相关资源
      最近更新 更多