2011-04-05 18 views
5

Scala es muy elegante en el filtrado de secuencias inmutables:Scala ArrayBuffer eliminar todos los elementos con un prédicat

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

pero ¿cómo puedo hacer esto con un colecciones mutables como ArrayBuffer? Todo lo que encontré es la eliminación de elementos individuales o sectores, o eliminar elementos de otra secuencia, pero nada que elimine elementos dados por un predicado.

Editar: Tenía la esperanza de encontrar algo similar aunque esto:

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

estos filtros en tiempo lineal y puede ser mezclado en cualquier tampón, pero sólo ArrayBuffer tiene sentido, en ListBuffers sería lento, porque la indexación toma tiempo lineal.

Respuesta

3

Supongo que es más eficiente filtrar creando un nuevo búfer, por lo que normalmente solo usaría filter y usaría su resultado. De lo contrario, puede escribir su propio método de filtro en el lugar:

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

Su 'filterInPlace' es lento, porque' b.Retire (i) 'es un método de O (n). –

1

Se ha debatido acerca de tener un conjunto de métodos que funcionan mediante la realización de mutación, pero la elaboración de un buen conjunto, genérico es sorprendentemente difícil, y, por otro lado, simplemente no ha habido suficiente demanda para ello.

+0

Supongo que no estoy exigiendo lo suficientemente fuerte, o hay muy pocos de mí. –

+0

@Rex Solo estoy esperando que los implemente y envíe a Scala Incubator. :-) Realísticamente, la gente de EPFL tiene recursos limitados, y algunas cosas importantes quedan excluidas. Vea el destino de '-save', por ejemplo. –

+0

Prácticamente me ofrecí a escribirlas en el hilo Scala-debate que comencé hace unos meses. Tal vez tengas razón y haya tan poca demanda que ni siquiera valga la pena pedirle a alguien que la implemente. –

0

A menudo withFilter es lo suficientemente bueno, especialmente si el Buffer se convierte en una estructura inmutable al final. Es cierto que en realidad no elimina los elementos, pero al menos no crea un nuevo objeto ArrayBuffer.

0

Esto funcionó para mí, pero sólo con el clon(), por lo tanto dejar de hacer un nuevo 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) 

Pero mejor manera habría que hacer una nueva gama de sólo aquellos elementos, que desea para eliminar (por lo tanto no copiar el búfer entero) y luego eliminarlos:

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

sí, su solución funcionaría, pero sería en el tiempo O (n^2), porque cada eliminación es una búsqueda y una reorganización del búfer.Estaba buscando algo que funciona en O (n). Pero creo que necesito hacerlo yo mismo. – Arne

+0

Creo que aquí en caso mutable nunca será O (N). Pero si haces un filtro inmutable clásico, entonces lo tienes en O (N) :-). –

1

lo haces lo mismo con ArrayBuffer. Todas las clases de colección tienen los mismos métodos disponibles.

1

me ocurrió con esta

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

} 
Cuestiones relacionadas