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