Подтвердить что ты не робот

Используя 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 и сравнения.

4b9b3361

Ответ 1

Boost Lambda делает это легко.

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

<ч/" > Update: Выполнение этого с синтаксисом лямбда С++ 0x также очень приятно (продолжение с предикатом по модулю 2):

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

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

Ответ 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;
    };

Ответ 3

Можно ли изменить вектор? Вы можете посмотреть на алгоритм разбиения.
Алгоритм разделения

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

Ответ 4

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) - главным образом потому, что vector гарантированно имеют итераторы с произвольным доступом.

Тем не менее, если вас беспокоит неэффективное 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);
}

Ответ 5

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

Ответ 6

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

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

Оригинальный пост здесь.

Ответ 7

Вы можете использовать Boost.Foreach:

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