#c #sorting #search
#c #сортировка #Поиск
Вопрос:
У меня есть структура, подобная этой:
struct client
{
string name;
double money;
};
У меня также есть 2 предиката:
bool less_10(const clientamp; a)
{
return a.money < 10;
}
bool not_a(const clientamp; a)
{
return a.name.at(0) != 'A';
}
В моей основной функции я использую это, чтобы отфильтровать результат, хранящийся в векторном client_list (все с деньгами < 10 (выбор 1) или все с именем, не начинающимся с A (else))
if (choice_filter == 1)
{
vector<client>::iterator it3;
it3 = find_if(client_list.begin(), client_list.end(), less_10);
while (it3 != client_list.end())
{
**client_list.erase(it3);
it3 = find_if(it3 1, client_list.end(), less_10);
}
client_list.erase(it3);**
}
else
{
vector<client>::iterator it4;
it4 = find_if(client_list.begin(), client_list.end(), not_a);
while (it4 != client_list.end())
{
**client_list.erase(it4);
it4 = find_if(it4 1, client_list.end(), not_a);
}
client_list.erase(it4);**
}
Я замечаю, что если я сначала удалю, а затем find_if , я потеряю последнего клиента. Итак, я добавил еще 1 строку для удаления, но программа вылетает, поскольку итератор теперь находится в конце, не может стереть.
Есть ли какой-либо способ обойти это? Я хочу продолжать использовать find_if с предикатами, а также цикл while, как указано выше, поскольку они требуются.
Комментарии:
1. Используйте
std::remove_if
вместо этого и вызывайтеerase
, используя результат. Это колесо, которое вы изобретаете заново.
Ответ №1:
Как говорили другие, std::remove_if
это лучшее решение. Если вы делаете это по педагогическим соображениям (что, как я подозреваю, имеет место, учитывая эти конкретные предикаты): вы на правильном пути. Единственная проблема заключается в том, что client_list.erase
делает недействительным итератор. Но поскольку он возвращает итератор к элементу сразу после элемента, который он стер, вы можете использовать что-то вроде:
std::vector<Client>::iterator it
= std::find_if( client_list.begin(), client_list.end(), predicate );
while ( it != client_list.end() ) {
it = client_list.erase( it );
it = std::find_if( it, client_list.end(), predicate );
}
И вы не хотите вызывать erase после цикла. Итератор
обозначает конец, где нет элемента, подлежащего удалению.
Ответ №2:
Типичный способ — использовать временный вектор:
vector<client> tmp;
for (...)
{
if(predicate(it))
tmp.push_back(*it);
}
client_list.swap(tmp);
Это похоже на то, что Крис предложил в комментарии, хотя это решение сначала переместило бы элементы в конец вектора, а затем усекло бы их оттуда. Я не уверен, что это не меняет порядок в пути, просто проверьте документацию. В зависимости от того, что вы хотите, любой из них может выполнить эту работу.
Если вы использовали другой контейнер, такой как list<>, который не аннулировал все итераторы в erase(), вы могли бы сделать это:
it = c.begin();
end = c.end();
while(it != end)
{
if(predicate(*it))
{
c.erase(it );
}
else
{
it;
}
}
Обратите внимание, что если вы вызываете erase(), вы все равно аннулируете этот итератор, следовательно, итератор сначала увеличивается, а erase() вызывается с прежним значением, используя приращение postfix.
Ответ №3:
Я также согласен с Крисом в использовании std::remove_if:
{
remove_if(client_list.begin(), client_list.end(), less_10);
}
Но если вы хотите изобрести колесо:
{
vector<client>::iterator it3 = client_list.begin();
while (true)
{
it3 = find_if(it3, client_list.end(), less_10);
if (it3 == client_list.end()) {
break;
}
it3 = client_list.erase(it3);
}
}
Комментарии:
1. Какой извращенный способ написания цикла. (И вы не объясняете, почему это решение не сработало.)