【问题标题】:scala ArrayBuffer remove all elements with a predicatscala ArrayBuffer 使用谓词删除所有元素
【发布时间】:2011-04-05 01:32:48
【问题描述】:

Scala 在过滤不可变序列方面非常优雅:

var l = List(1,2,3,4,5,6)
l = l.filter(_%2==1)

但是如何使用像 ArrayBuffer 这样的可变集合来做到这一点? 我发现的只是删除单个元素或切片,或从另一个序列中删除元素,但没有删除谓词给出的元素。

编辑: 我希望能找到类似的东西:

trait Removable[A] extends Buffer[A]{ 
  def removeIf(p: A => Boolean){
    var it1 = 0
    var it2 = 0

    while(it2 < length){
      if( p( this(it2) ) ){
        it2 += 1;
      } 
      else {
        this(it1) = this(it2)
        it1 += 1;
        it2 += 1;
      }
    }

    trimEnd(it2-it1)
  }
}

这个过滤器是线性时间的,可以混合到任何缓冲区中,但只有 ArrayBuffer 才有意义,在 ListBuffers 上它会很慢,因为索引确实需要线性时间。

【问题讨论】:

    标签: scala arraybuffer


    【解决方案1】:

    我的猜测是通过构建新缓冲区进行过滤会更有效,因此您通常只需使用 filter 并使用其结果。否则,您可以编写自己的就地过滤方法:

    def filterInPlace[A](b: collection.mutable.Buffer[A])(fun: A => Boolean): Unit = {
      var sz = b.size
      var i = 0; while(i < sz) {
        if (fun(b(i))) {
          i += 1
        } else {
          sz -= 1
          b.remove(i)
        }
      }
    }
    
    val b = collection.mutable.ArrayBuffer((1 to 6): _ *)
    filterInPlace(b)(_ % 2 == 1)
    println(b)
    

    【讨论】:

    • 你的 filterInPlace 很慢,因为 b.remove(i) 是一个 O(n) 方法。
    【解决方案2】:

    关于拥有一组通过执行突变来工作的方法的讨论,但想出一个好的、通用的集非常困难,而且另一方面,对它的需求还不够。

    【讨论】:

    • 我想我要求的声音不够大,或者我的人太少了。
    • @Rex 我只是在等你实现它们并提交给 Scala 孵化器。 :-) 实际上,EPFL 人员的资源有限,一些重要的东西被遗漏了。例如,查看-save 的命运。
    • 我实际上提出在几个月前开始的 scala-debate 线程中自己编写它们。也许你是对的,因为需求太少,甚至不值得让别人来实现它。
    • @Rex 我以前使用过开源软件,所以我可以给你他们可能的观点。建议是无限的,提供的帮助比比皆是。然而,很少有人进入另一边,添加到代码库的任何代码都是在该代码库上工作的额外负担。因此,即使是一个完整的实现也会被谨慎看待,但是,它是获得某些东西的唯一有效方法:在提供它之前完成所有工作。
    • 确实如此。不幸的是,我只有足够的时间来处理很有可能被使用的事情。
    【解决方案3】:

    ArrayBuffer 也是如此。所有集合类都有相同的可用方法。

    【讨论】:

      【解决方案4】:

      我想出了这个

      import scala.collection.mutable
      
      trait BufferUtils {
          import BufferUtils._
          implicit def extendMutableBuffer[T](org: mutable.Buffer[T]): ExtendedBuffer[T] = new ExtendedBuffer(org)
      }
      
      object BufferUtils extends BufferUtils {
      
          implicit class ExtendedBuffer[T](val org: mutable.Buffer[T]) extends AnyVal {
              def removeIf(pred: (T) => Boolean): Unit = {
                  // target holds the index we want to move the next element to
                  var target = 0
      
                  for (i <- org.indices;
                       elem = org(i)
                       if !pred(elem)) {
      
                      org(target) = elem
                      target += 1
                  }
      
                  org.remove(target, org.size - target)
              }
          }
      
      }
      

      【讨论】:

        【解决方案5】:

        通常withFilter 已经足够好了,尤其是当 Buffer 最终转换为不可变结构时。没错,它并没有真正删除元素,但至少它没有创建新的 ArrayBuffer 对象。

        【讨论】:

          【解决方案6】:

          这对我有用,但仅适用于 clone(),因此仍会创建一个新的 ArrayBuffer :-)

          scala> import collection.mutable.ArrayBuffer
          import collection.mutable.ArrayBuffer
          
          scala> val buf = ArrayBuffer(1,2,3,4,5,6,7,8,9,10)
          buf: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
          
          scala> buf.clone foreach { x => if (x > 4) buf -= x }
          
          scala> buf
          res1: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 4)
          

          但更好的方法是创建一个仅包含您要删除的元素的新数组(因此不复制整个缓冲区),然后删除它们:

          scala> val buf = ArrayBuffer(1,2,3,4,5,6,7,8,9,10)
          buf: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
          
          scala> buf filter { _ > 4 } foreach { buf -= _ }
          
          scala> buf
          res3: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 4)
          

          【讨论】:

          • 是的,您的解决方案会起作用,但它会及时 O(n^2),因为每次删除都是对缓冲区的搜索和重新排列。我一直在寻找在 O(n) 中有效的东西。但我想我需要自己做。
          • 我认为在可变情况下它永远不会是 O(N)。但是如果你做经典的不可变过滤器,那么你就有它在 O(N) :-)。
          猜你喜欢
          • 2012-02-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-01-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多