использование STL для поиска всех элементов в векторе

у меня есть коллекция элементов, с которыми мне нужно работать, вызывая функции-члены в коллекции:

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

для вызова функций без аргументов это довольно прямолинейно:

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

аналогичная вещь может быть сделана, если есть один аргумент функции, которую я хочу вызвать.

моя проблема в том, что я хочу вызвать функцию на элементах в векторе, если она удовлетворяет некоторому условию. std::find_if возвращает итератор к первому элементу соответствие условиям предиката.

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

Я хочу найти все элементы, удовлетворяющие предикату и работать над ними.

Я смотрел на алгоритмы STL для"find_all" или "do_if" эквивалент или способ, которым я могу сделать это с существующим STL (таким, что мне нужно только повторить один раз), а не сворачивать свой собственный или просто выполнять стандартную итерацию, используя цикл for и сравнения.

7 ответов


Boost лямбда делает это легко.

#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) ] 
             );

вы даже можете отказаться от определения MyPred (), если это просто. Вот где действительно сияет лямбда. Например, если MyPred означает "делится на 2":

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


обновление: Делать это с синтаксисом лямбда C++0x также очень приятно (продолжая с предикатом по модулю 2):
std::for_each( v.begin(), v.end(),
               [](MyType& mt ) mutable
               {
                 if( mt % 2 == 0)
                 { 
                   mt.myfunc(); 
                 }
               } );

на первый взгляд это выглядит как шаг назад от синтаксиса boost::lambda, однако, это лучше, потому что более сложная логика функтора тривиальна для реализации с синтаксисом c++0x... где что-то очень сложное в boost::lambda быстро становится сложным. Microsoft Visual Studio 2010 beta 2 в настоящее время реализует эту функцию.


Я написал for_each_if() и for_each_equal() которые делают то, что я думаю, вы ищете.

for_each_if() принимает функтор предиката для оценки равенства и for_each_equal() принимает значение любого типа и выполняет прямое сравнение с помощью operator ==. В обоих случаях функция, которую вы передаете, вызывается для каждого элемента, который проходит тест равенства.

/* ---

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

можно ли изменить вектор? Возможно, вы захотите взглянуть на алгоритм разбиения.
алгоритм раздела

другой вариант-изменить ваш MyType::myfunc либо проверить элемент, либо взять предикат в качестве параметра и использовать его для проверки элемента, на котором он работает.


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

для записи, в то время как я видел реализации, где вызов end() на list был O( n), я не видел никаких реализаций STL, где вызов end() на vector было что угодно, кроме O (1) -- главным образом потому, что vectors гарантированно имеют итераторы произвольного доступа.

даже так, если вы беспокоитесь о неэффективном end(), вы можете использовать этот код:

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

для чего это стоит for_each_if рассматривается как возможное дополнение к росту. Нетрудно реализовать свой собственный.


Lamda функции-идея состоит в том, чтобы сделать что-то вроде этого

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

Origial post здесь.


можно использовать импульс.По каждому элементу:

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