Я хотел бы удалить элементы из вектора, используя remove_if
функция, но ограничение стирания до N элементов.
Пример:
// predicate function that determines if a value is an odd number.
bool IsOdd (int i) {
if (we deleted more than deleteLimit)
return false;
return ((i%2)==1);
}void otherFunc(){
int deleteLimit = 10;
// remove odd numbers:
std::vector<int>::iterator newEnd =
std::remove_if (myints.begin(), myints.end(), IsOdd (how to pass deleteLimit?) );
}
я нуждаюсь в этом IsOdd
Предикат хранит, сколько элементов он удалил и сколько мы хотим удалить.
Единственный способ — использовать глобальную переменную? Как это:
int deleteLimit = 10;
int removedSoFar = 0;
bool IsOdd (int i) {
if (deleteLimit < removedSoFar)
return false;
if (i%2==1) {
removedSoFar++
return true;
}
return false;
}
remove_if ...
Состояние, указывающее, «сколько элементов было удалено до сих пор», должно быть определено вне функции / вызова алгоритма. Это потому, что у функтора не должно быть состояния, которое изменяется при вызове (это было бы неопределенным поведением).
Вы должны взять ссылку на это состояние (счетчик) в конструкторе функтора (или захватить по ссылке в лямбде), чтобы вы могли получить доступ и изменить этот счетчик. Когда этот функтор теперь копируется, не имеет значения, какой из них вызывается алгоритмом, поскольку все они теперь содержат ссылку на то же состояние.
Использование функторов (C ++ 03):
class IsOdd {
int deleteLimit;
int & deletedSoFar;
public:
IsOdd(int deleteLimit, int & deletedSoFar) :
deleteLimit(deleteLimit), deletedSoFar(deletedSoFar)
{}
bool operator()(int i) const {
if (deletedSoFar < deleteLimit && i % 2) {
++deletedSoFar;
return true;
}
return false;
}
};
int deletedSoFar = 0;
int deleteLimit = 10;
std::remove_if (myints.begin(), myints.end(), IsOdd(deleteLimit, deletedSoFar));
Использование лямбды (C ++ 11):
int deletedSoFar = 0;
int deleteLimit = 10;
auto it = std::remove_if (myints.begin(), myints.end(), [deleteLimit,&deletedSoFar](int i){
if (deletedSoFar < deleteLimit && i % 2) {
++deletedSoFar;
return true;
}
return false;
});
myints.erase(it, myints.end());
Помимо создания собственного функтора, вы можете передать лямбда-выражение:
auto deleteLimit = 25;
auto removedSoFar = 0;
auto it = remove_if (myints.begin(),
myints.end(),
[deleteLimit, &removedSoFar](int i)->bool
{
if ( (deletedSoFar < deleteLimit) && (i % 2)) {
++deletedSoFar;
return true;
}
return false;
} );
// really remove the elements from the container
myints.erase(it, myints.end());
Однако остерегайтесь того, что функторы с сохранением состояния и алгоритмы библиотеки std не всегда хорошо сочетаются. Здесь, звонки в лямбду могут иметь побочный эффект, поэтому у вас нет гарантии на который элементы в последовательности будут удалены.
Обратите внимание на последний звонок std::vector::erase
, Это необходимо для того, чтобы действительно удалить ненужные элементы из контейнера. Увидеть стереть удалить идиома.
int initial_size = myints.size();
std::remove_if (myints.begin(), myints.end(), [myints.size(),limit,initial_size](int i)->bool{
return ( ( initial_size-myints.size() )<limit ? i%2 : false) } );
использование functor
, структура с operator ()
или используйте некоторые функции связывания (std :: bind / boost :: bind).
bool isOdd(int current, int limit, int& count)
{
if (count >= limit)
{
return false;
}
if (current % 2 == 1)
{
++count;
return true;
}
return false;
}
int count = 0, limit = 10;
vec.erase(std::remove_if(vec.begin(), vec.end(),
std::bind(&isOdd, _1, limit, std::ref(count)), vec.end());
И с функтором
struct IsOdd : public std::unary_function<int, bool>
{
public:
IsOdd(int l) : count(0), limit(l)
{
}
bool operator () (int i)
{
if (count >= limit)
{
return false;
}
if (current % 2 == 1)
{
++count;
return true;
}
return false;
private:
int count;
int limit;
};
int limit = 10;
vec.erase(std::remove_if(vec.begin(), vec.end(),
isOdd(limit)), vec.end());
Код в проголосовавшем ответе имеет небольшую ошибку. Перед ключевым словом оператора отсутствуют круглые скобки. Я не мог сделать правку, поскольку в ней меньше шести символов, и я не мог комментировать, так как мой счет слишком низкий.
class IsOdd {
int deleteLimit;
int & deletedSoFar;
public:
IsOdd(int deleteLimit, int & deletedSoFar) :
deleteLimit(deleteLimit), deletedSoFar(deletedSoFar)
{}
bool operator()(int i) const {
if (deletedSoFar < deleteLimit && i % 2) {
++deletedSoFar;
return true;
}
return false;
}
};
int deletedSoFar = 0;
int deleteLimit = 10;
std::remove_if (myints.begin(), myints.end(), IsOdd(deleteLimit, deletedSoFar));