2011-04-05 53 views
5

Scala是非常優雅的過濾不變的序列:斯卡拉ArrayBuffer刪除與predicat所有元素

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這將是緩慢的,因爲索引確實需要線性時間。

回答

3

我的猜測是,這是更有效的通過建立一個新的緩衝區進行過濾,所以你通常只用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) 
+1

你'filterInPlace'是緩慢的,因爲'b.remove(我)'是O(n)的方法。 – 2013-08-02 09:44:07

1

已經有大約有一組通過對突變的工作方法,但想出一個好的,一組通用的討論是出奇的難,並且,另一方面,對此沒有足夠的需求。

+0

我想我不是要求很高的聲音,或者我的太少了。 – 2011-04-05 11:24:41

+0

@Rex我只是等待着你去實現它們,並提交到Scala的孵化器。 :-)實際上,EPFL人員資源有限,一些重要的東西被排除在外。例如,查看'-save'的命運。 – 2011-04-05 14:20:40

+0

我幾乎提供給他們寫自己在斯卡拉議線程我開始在幾個月前。也許你是對的,需求如此之少,甚至不值得讓別人去實現它。 – 2011-04-05 14:30:20

0

通常withFilter是不夠好,尤其是如果緩衝器被轉換成不可變結構中的端部。誠然,它並沒有真正移除元素,但至少它不會創建新的ArrayBuffer對象。

0

這爲我工作,但只克隆(),因此仍然作出新的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) 
+0

是的,你的解決方案將工作,但它會在時間O(n^2),因爲每個刪除是一個搜索和緩衝區的重新排列。我正在尋找O(n)中有效的東西。但我認爲我需要自己做。 – Arne 2011-04-05 13:27:55

+0

我覺得在這裏可變的情況下它永遠不會是O(N)。但是,如果你經典的不可變過濾器,那麼你在O(N):-)。 – 2011-04-06 19:02:42

1

您可以使用ArrayBuffer。所有集合類都有相同的可用方法。

1

我想出了這個

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) 
     } 
    } 

}