2008-10-24 22 views
12

Tengo una colección de elementos que necesito para operar sobre, llamando a las funciones miembro de la colección:usando STL para encontrar todos los elementos de un vector

std::vector<MyType> v; 
... // vector is populated 

Para las llamadas a funciones con argumentos que es bastante recta hacia adelante :

std::for_each(v.begin(), v.end(), std::mem_fun(&MyType::myfunc)); 

algo similar se puede hacer si hay un argumento a la función que desea llamar.

Mi problema es que quiero llamar a una función en elementos en el vector si cumple alguna condición. std::find_if devuelve un iterador al primer elemento que cumple las condiciones del predicado.

std::vector<MyType>::iterator it = 
     std::find_if(v.begin(), v.end(), MyPred()); 

Deseo encontrar todos elementos que cumplen el predicado y operar sobre ellos.

He estado buscando en los algoritmos de STL para un "find_all" o "do_if" equivalente, o alguna manera de hacer esto con la STL existente (por ejemplo que sólo tengo que repetir una vez), en lugar de rodar mi propio o simplemente hacer una iteración estándar usando un bucle for y comparaciones.

Respuesta

19

Boost Lambda lo hace fácil.

#include <boost/lambda/lambda.hpp> 
#include <boost/lambda/bind.hpp> 
#include <boost/lambda/if.hpp> 

std::for_each(v.begin(), v.end(), 
       if_(MyPred())[ std::mem_fun(&MyType::myfunc) ] 
      ); 

Incluso puede eliminar la definición de MyPred(), si es simple. Aquí es donde realmente brilla lambda. Por ejemplo, si MyPred significaba "es divisible por 2":

std::for_each(v.begin(), v.end(), 
       if_(_1 % 2 == 0)[ std::mem_fun(&MyType::myfunc) ] 
      ); 


Actualización: Hacer esto con la sintaxis lambda C++ 0x es también muy agradable (continuando con el predicado como módulo 2):

std::for_each(v.begin(), v.end(), 
       [](MyType& mt) mutable 
       { 
       if(mt % 2 == 0) 
       { 
        mt.myfunc(); 
       } 
       }); 

a primera vista esto parece un paso atrás de la sintaxis boost :: lambda, sin embargo, es mejor porque la lógica funtor más complejo es trivial de implementar con C++ 0x sintaxis ...donde algo muy complicado en boost :: lambda se vuelve complicado rápidamente. Microsoft Visual Studio 2010 beta 2 actualmente implementa esta funcionalidad.

6

¿Está bien para cambiar el vector? Es posible que desee ver el algoritmo de partición.
Partition algorithm

Otra opción sería la de cambiar su MyType::myfunc a cualquiera comprobar el elemento, o para tomar un predicado como parámetro y lo utilizan para probar el elemento se está operando.

+0

Partición funcionaría en un caso general, pero para este caso concreto, no funcionará, ya que el vector es invariable . – twokats

12

Escribí un for_each_if() y un for_each_equal() que hacen lo que creo que estás buscando.

for_each_if() toma un funtor predicado para evaluar la igualdad, y for_each_equal() toma un valor de cualquier tipo y hace una comparación directa utilizando operator ==. En ambos casos, la función que pasa se invoca en cada elemento que pasa la prueba de igualdad.

/* --- 

    For each 
    25.1.1 

     template< class InputIterator, class Function, class T> 
      Function for_each_equal(InputIterator first, InputIterator last, const T& value, Function f) 

     template< class InputIterator, class Function, class Predicate > 
      Function for_each_if(InputIterator first, InputIterator last, Predicate pred, Function f) 

    Requires: 

     T is of type EqualityComparable (20.1.1) 

    Effects:  

     Applies f to each dereferenced iterator i in the range [first, last) where one of the following conditions hold: 

      1: *i == value 
      2: pred(*i) != false 

    Returns:  

     f 

    Complexity: 

     At most last - first applications of f 

    --- */ 

    template< class InputIterator, class Function, class Predicate > 
    Function for_each_if(InputIterator first, 
         InputIterator last, 
         Predicate pred, 
         Function f) 
    { 
     for(; first != last; ++first) 
     { 
      if(pred(*first)) 
       f(*first); 
     } 
     return f; 
    }; 

    template< class InputIterator, class Function, class T> 
    Function for_each_equal(InputIterator first, 
          InputIterator last, 
          const T& value, 
          Function f) 
    { 
     for(; first != last; ++first) 
     { 
      if(*first == value) 
       f(*first); 
     } 
     return f; 
    }; 
+0

+1 para publicar código de trabajo que hace exactamente lo que estaba buscando. –

+0

Gracias. Wow, esto fue un viejo! –

0

Por lo que su valor for_each_if está siendo considerada como una adición posterior a impulsar. No es difícil implementar el tuyo.

0

funciones Lamda - la idea es hacer algo como esto

for_each(v.begin(), v.end(), [](MyType& x){ if (Check(x) DoSuff(x); }) 

Origial publicar here.

0

Puede utilizar Boost.Foreach:

BOOST_FOREACH (vector<...>& x, v) 
{ 
    if (Check(x) 
     DoStuff(x); 
} 
+0

También puedo usar un bucle for tradicional. Esto no resuelve el problema de querer utilizar algoritmos STL para escribir una versión más limpia de for_each con un predicado. Lamdba resuelve este problema con bastante elegancia. – twokats

1
std::vector<int> v, matches; 
std::vector<int>::iterator i = v.begin(); 
MyPred my_pred; 
while(true) { 
    i = std::find_if(i, v.end(), my_pred); 
    if (i == v.end()) 
     break; 
    matches.push_back(*i); 
} 

Para el registro, mientras que yo he visto una implementación en la llamada end() en un list estaba O (n), no he visto ninguna de las implementaciones donde STL llamar a end() en un vector fue cualquier cosa que no sea O (1), principalmente porque vector s tienen la garantía de tener iteradores de acceso aleatorio.

Aún así, si usted está preocupado por una ineficiente end(), puede utilizar este código:

std::vector<int> v, matches; 
std::vector<int>::iterator i = v.begin(), end = v.end(); 
MyPred my_pred; 
while(true) { 
    i = std::find_if(i, v.end(), my_pred); 
    if (i == end) 
     break; 
    matches.push_back(*i); 
} 
+0

Puede obtener la mitad de la complejidad de este ciclo al encontrar_if de i a v.end(). Sigue siendo cuadrático, aunque ... -1 – xtofl

+0

Gracias por señalar mi error en el código original. El código ahora es O (n): comience desde el principio -> busque la primera coincidencia y guárdela -> comenzando desde esa posición, busque la siguiente coincidencia y guárdela -> continúe hasta llegar al final. –

+0

¿No agregará este código el primer elemento de * v * a * coincidencias *, independientemente de * MyPred *? –

Cuestiones relacionadas