Wind:
> std::map <int, СЛОЖНАЯ_СТРУКТУРА> <...> задача удаления не нужных записей <...> > 1) Перебираю каждый элемент списка на соответствие некоторым условиям > 2) Встретился элемент, который нужно удалить — удалил его и продолжил искать далее > Но вот незадача, после удаления элемента итераторы становятся не действительными
Только непосредственно удаленный.
> Что псоветуете?
for (MyMap::iterator i = m.begin(), e = m.end(); i != e; /* здесь пусто */ )
{
if ( . . . )
m.erase(i++);
else++i;
}
Posted via RSDN NNTP Server 1.9 gamma
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Return value:
a bidirectional iterator that designates the first element remaining beyond any elements removed, or a pointer to the end of the map if no such element exists
Следовательно делаем так
for (it = map.begin();i != map.end();it++)
{
if (условие)
{
it = map.erase(it); // it будет переброшен на первый верхний неизмененный елемент
}
}
Успехов
Здравствуйте, Wind,
Вы писали:
W>у меня есть std::map <int, СЛОЖНАЯ_СТРУКТУРА> a. Периодически возникает задача удаления не нужных записей, причем таких записей может быть несколько и расположены они могут быть в произвольных местах. Алгоритм, который мне пришел в голову: W>1) Перебираю каждый элемент списка на соответствие некоторым условиям W>2) Встретился элемент, который нужно удалить — удалил его и продолжил искать далее
W>Но вот незадача, после удаления элемента итераторы становятся не действительными, значит их нужно задать заново и с элемента .begin() заново начать поиск (заново, т.к. ни кто гарантий не дает, как будут располагаться элементы. Или я не прав?) Это потенциально долго, а на ключи полагаться нельзя, т.к. в момент работы алгоритма значения ключей не известны.
W>Вот такая ерунда. W>Что псоветуете?
Sasparella:
> ПК> Только непосредственно удаленный.
> Угу, согласен — но имхо на это полагаться не стоит — ибо потом контейнер может смениться (ну, например на тот же вектор) — и искать багу придется очень долго....
Пытаться писать "контейнеро-независимый" код — дохлый номер. За исключением редких случаев, и у меня ощущение, что данный к ним не относится.
Posted via RSDN NNTP Server 1.9 gamma
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
При этом будет регулярно пропускаться один элемент после удаляемого
S>Привет S>в доке все написано
S>Iterator map::erase (iterator Where)
S>Return value: S>a bidirectional iterator that designates the first element remaining beyond any elements removed, or a pointer to the end of the map if no such element exists S>Следовательно делаем так
S>
S>for (it = map.begin();i != map.end();it++)
S>{
S> if (условие)
S> {
S> it = map.erase(it); // it будет переброшен на первый верхний неизмененный елемент
S> }
S>}
S>
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, itman, Вы писали:
S>>>в доке все написано
К>Эта дока называется MSDN и писана по Dinkumware STL, которая вот именно в этом месте отходит от Стандарта. К>По Стандарту, void map::erase(map::iterator).
К>А это не спасёт: добыча итератора на следующий за удалённым элемент в дереве — такая же дорогостоящая, как и простой инкремент. Ну может быть, чуть-чуть дешевле, если после перебалансировки расстояние между элементами в дереве сократилось. К>Ну а если говорить точно, то время инкремента — O(log N).
имеется в виду, что при конструкции, не требующей нового итератора, его не вычислять:
m.erase(it);//ну нужен новый итератор
если же нужен новый итератор, то будь добр позаботиться об этом сам
у меня есть std::map <int, СЛОЖНАЯ_СТРУКТУРА> a. Периодически возникает задача удаления не нужных записей, причем таких записей может быть несколько и расположены они могут быть в произвольных местах. Алгоритм, который мне пришел в голову:
1) Перебираю каждый элемент списка на соответствие некоторым условиям
2) Встретился элемент, который нужно удалить — удалил его и продолжил искать далее
Но вот незадача, после удаления элемента итераторы становятся не действительными, значит их нужно задать заново и с элемента .begin() заново начать поиск (заново, т.к. ни кто гарантий не дает, как будут располагаться элементы. Или я не прав?) Это потенциально долго, а на ключи полагаться нельзя, т.к. в момент работы алгоритма значения ключей не известны.
В похожей ситуайии я ничего умнее не придумал как пробежать по мепу, накопить во временном векторе ключи, соответсвующие структурам, подлежащим удалению, а потом пробежался по этому вектору, и сделал для каждого ключа в векторе map::erase(key).
Угу, согласен — но имхо на это полагаться не стоит — ибо потом контейнер может смениться (ну, например на тот же вектор) — и искать багу придется очень долго....
ПК>>Только непосредственно удаленный.
S>Угу, согласен — но имхо на это полагаться не стоит — ибо потом контейнер может смениться (ну, например на тот же вектор) — и искать багу придется очень долго....
Здравствуйте, Wind, Вы писали:
W>у меня есть std::map <int, СЛОЖНАЯ_СТРУКТУРА> a. Периодически возникает задача удаления не нужных записей, причем таких записей может быть несколько и расположены они могут быть в произвольных местах. Алгоритм, который мне пришел в голову: W>1) Перебираю каждый элемент списка на соответствие некоторым условиям W>2) Встретился элемент, который нужно удалить — удалил его и продолжил искать далее
W>Но вот незадача, после удаления элемента итераторы становятся не действительными, значит их нужно задать заново и с элемента .begin() заново начать поиск (заново, т.к. ни кто гарантий не дает, как будут располагаться элементы. Или я не прав?) Это потенциально долго, а на ключи полагаться нельзя, т.к. в момент работы алгоритма значения ключей не известны.
W>Вот такая ерунда. W>Что псоветуете?
Господа, поправте если ошибаюсь. При добавлении элемента в map элемент автоматически размещается в нужную позицию в соответствии с его ключом. Соответственно, элементы с одинаковым ключём должны размещаться рядом (при этом их может быть несколько).
m_map.erase(key) удалит все элементы с заданым ключом. Для чего нужен цикл, не понятно.
Здравствуйте, itman, Вы писали:
I>Здравствуйте, Павел Кузнецов, Вы писали:
I>У меня вопро на засыпку: I>
I> m.erase(i++);
I>
I>и I>
I> i = m.erase(i);
I>
I>дадут одинаковые значения итератора?
Да, одинаковые. I>если да, то зачем, вообще, из ерейза что-то возвращать?
Возможно, разработчики реализации STL для VC6 посчитали, что это имеет смысл (erse для вектора, например, возвращает итератор). По стандарту erase для map возвращает void.
топик.
__>Господа, поправте если ошибаюсь. При добавлении элемента в map элемент автоматически размещается в нужную позицию в соответствии с его ключом. Соответственно, элементы с одинаковым ключём должны размещаться рядом (при этом их может быть несколько).
__>m_map.erase(key) удалит все элементы с заданым ключом. Для чего нужен цикл, не понятно.
map не сожержит дуприкатов ключей. Этим занимается multimap.
B>map не сожержит дуприкатов ключей. Этим занимается multimap.
ok
С уважением,
Денис
Re[3]: map и удаление элементов.
От:
Аноним
Дата:
28.10.04 07:10
Оценка:
Здравствуйте, _wind_, Вы писали:
__>Здравствуйте, itman, Вы писали:
I>>Здравствуйте, Павел Кузнецов, Вы писали:
ПК>>>
ПК>>>for (MyMap::iterator i = m.begin(), e = m.end(); i != e; /* здесь пусто */ )
ПК>>>{
ПК>>> if ( . . . )
ПК>>> m.erase(i++);
ПК>>> else
ПК>>> ++i;
ПК>>>}
ПК>>>
__>"Скотт Майрес", Эффективное использование STL, страница 52
тааак, открываю страницу 52 и что я вижу. есессно облом. когда посылаешь неплохо указать точное направление, (намёк издание, год, итд). впрочем, я догадываюсь, что ты хотел этим сказать. ну ладно, тогда правильее было бы переписать цикл так:
for (MyMap::iterator i = m.begin(), e = m.end(); i != e; /* здесь пусто */ )
{
if ( . . . )
i = m.erase(i);
else++i;
}
а теперь таки по поводу конструкции еще раз
m.erase(i++);
i++ возвращает копию итератора i, для erase, после чего наступает sequcne point и i инкрементируется. а уж потом делается erase. вроде все работает правильно, но совершенно неочевидно.
А>i++ возвращает копию итератора i, для erase, после чего наступает sequcne point и i инкрементируется. а уж потом делается erase. вроде все работает правильно, но совершенно неочевидно.
инкремент выполнится до начала выполнения функции, но после передачи параметра в функцию
Кстати, когда-то давно встречался с задачей реализовать свою функцию void* malloc(size_t s)? Может кто-нибудь дать ссылки по теме? Ещё интересует реализация функций работы с кучей на базе массива. Хотелось бы просветиться прежде чем копаться в исходниках(malloc.c etc.) Tnx!
Здравствуйте, Wind, Вы писали:
W>Но вот незадача, после удаления элемента итераторы становятся не действительными, значит их нужно задать заново и с элемента .begin() заново начать поиск (заново, т.к. ни кто гарантий не дает, как будут располагаться элементы. Или я не прав?) Это потенциально долго, а на ключи полагаться нельзя, т.к. в момент работы алгоритма значения ключей не известны.
Ты не прав.
Контейнеры list, set, map не переупорядочивают элементы при удалении/вставке, и не перемещают их в памяти.
То есть все итераторы (кроме относящихся к удаляемым) остаются валидными, а порядок следования сохраняется.
Таким образом, алгоритм удаления по условию будет выглядеть так
struct no_action
{
template<class T> void operator()(T& t) const {}
};
template<class C, class P, class U>
void erase_if(C& cont, P pred, U utilize = no_action())
{
C::iterator i=cont.begin(), j, e=cont.end();
while(i!=e)
{
if(pred(*i))
{
j=i; // запомним итератор на текущий элемент
++i; // вскоре текущая позиция будет невалидна, поэтому спешим оттуда убраться
utilize(*j);
cont.erase(j);
}
else
++i;
}
}
Другое дело, что может оказаться, что удаление элемента сопровождается добавлением других (из недр кода деструктора).
В этом случае, чтобы не зациклиться, может оказаться полезным двухфазное удаление: сперва получить список итераторов на удаляемые элементы, затем их всех перестрелять.
Вообще-то правильное удаление из map — это erase по ключу. Каков
критерий удаления? Определяется по содержимому СЛОЖНОЙ_СТРУКТУРЫ? Тогда
стоит попытаться формировать список ключей удаляемых элементов
(set<int>) и проходить по нему, удаляя соотв. элементы map.
Wind wrote:
> у меня есть std::map <int, СЛОЖНАЯ_СТРУКТУРА> a. Периодически > возникает задача удаления не нужных записей, причем таких записей > может быть несколько и расположены они могут быть в произвольных > местах. Алгоритм, который мне пришел в голову: 1) Перебираю каждый > элемент списка на соответствие некоторым условиям 2) Встретился > элемент, который нужно удалить — удалил его и продолжил искать далее
Здравствуйте, TheBeard, Вы писали:
TB>Вообще-то правильное удаление из map — это erase по ключу. Каков TB>критерий удаления? Определяется по содержимому СЛОЖНОЙ_СТРУКТУРЫ? Тогда TB>стоит попытаться формировать список ключей удаляемых элементов TB>(set<int>) и проходить по нему, удаляя соотв. элементы map.
В любом случае ты сперва тратишь O(N) времени на обход контейнера от begin() до end(), получая либо список итераторов, либо список ключей элементов, подлежащих удалению.
Затем ищешь каждый элемент за время O(1) если итератор, либо O(log N) если ключ — выполняется поиск.
После чего удаляешь элемент за O(log N) (перебалансировка).
Вопрос: зачем платить больше?
Перекуём баги на фичи!
Re[3]: Map и удаление элементов.
От:
Аноним
Дата:
28.10.04 11:25
Оценка:
Почему пропускться
на следующей итерации for выполнит it++
и все будет ок пример
мар:
1, объект
2, объект
3, объект
4, обьъект
на каком-то шаге
it = 3
it = map.erase(it); // после этого it =2
continue; // следующий элемент уже it =4 после того как for выполнит it ++
Здравствуйте, itman, Вы писали:
I>Здравствуйте, serega, Вы писали:
I>При этом будет регулярно пропускаться один элемент после удаляемого
S>>Привет S>>в доке все написано
S>>Iterator map::erase (iterator Where)
S>>Return value: S>>a bidirectional iterator that designates the first element remaining beyond any elements removed, or a pointer to the end of the map if no such element exists S>>Следовательно делаем так
S>>
S>>for (it = map.begin();i != map.end();it++)
S>>{
S>> if (условие)
S>> {
S>> it = map.erase(it); // it будет переброшен на первый верхний неизмененный елемент
S>> }
S>>}
S>>
Здравствуйте, itman, Вы писали:
S>>в доке все написано
Эта дока называется MSDN и писана по Dinkumware STL, которая вот именно в этом месте отходит от Стандарта.
По Стандарту, void map::erase(map::iterator).
S>>for (it = map.begin();i != map.end();it++)
S>>{
S>> if (условие)
S>> {
S>> it = map.erase(it); // it будет переброшен на первый верхний неизмененный елемент
S>> }
S>>}
В этом коде — ошибка. После того, как будет получен итератор на следующий за удаляемым элемент, мы на следующем шаге его инкрементируем.
MAP coll; //MAP - тайпдеф мепа.
MAP::iterator pos;
for(pos = coll.begin(); pos!=coll.end(); ++pos)
if(pos->second==value)
coll.erase(pos); //ошибка времени выполнения
Полсе вызова erase() для элемента, на который ссылается итератор, итератор становится не действительным. Любые попытки использовать этот итератор после удаления элемента без повторной инициализации — даже команда ++pos — приведет к непредсказуемым последствиям.
Согласен. Верю. Понимаю.
Правильный способ удаления элемента, на который ссылается итератор, выглядит так:
Здравствуйте, Wind, Вы писали:
W>Код идентичен вашему.
Нет, не идентичен.
W>Но лично я не понимаю в чем разница между примерами. Какая разница где итератор инкреминировать?
Не "где" а "когда" и "какой".
Давай заменим for() на эквивалентный while() — так будет понятнее
// твой код
pos = coll.begin();
while(pos != coll.end())
{
if( to_be_deleted(*pos) )
coll.erase(pos);
++pos; // если мы только что инвалидировали итератор (стерев элемент), то не можем его инкрементировать
}
// правильный код
pos = coll.begin();
while(pos != coll.end())
{
if( to_be_deleted(*pos) )
coll.erase(pos++);
else
++pos;
}
// поскольку там фигурирует постинкремент, распишем его
pos = coll.begin();
while(pos != coll.end())
{
if( to_be_deleted(*pos) )
{
temp = pos; ++pos; // вот что такое постинкремент
coll.erase(temp); // тем самым мы никогда не будем иметь дело с инвалидным pos
}
else
++pos;
}
Здравствуйте, Анатолий Широков, Вы писали:
АШ>Посмотрите как реализована операция постинкремента:
АШ>А теперь взгляните на
АШ>erase принимает старое значение pos (*) и именно старое значение (*) становится невалидным.
Понял. Спасибо.
Здравствуйте, Wind, Вы писали:
W>у меня есть std::map <int, СЛОЖНАЯ_СТРУКТУРА> a. Периодически возникает задача удаления не нужных записей, причем таких записей может быть несколько и расположены они могут быть в произвольных местах. Алгоритм, который мне пришел в голову: W>1) Перебираю каждый элемент списка на соответствие некоторым условиям W>2) Встретился элемент, который нужно удалить — удалил его и продолжил искать далее
W>Но вот незадача, после удаления элемента итераторы становятся не действительными, значит их нужно задать заново и с элемента .begin() заново начать поиск (заново, т.к. ни кто гарантий не дает, как будут располагаться элементы. Или я не прав?) Это потенциально долго, а на ключи полагаться нельзя, т.к. в момент работы алгоритма значения ключей не известны.
W>Вот такая ерунда. W>Что псоветуете?
Вот буквально пару дней назад у меня именно такая задача стояла в дипломе.
Кстати теперь у меня много времени на диплом (в отличие от 4 дня назад) еще целый год, так что жду поздравлений
В целом все то же самое
std::map<int/*иногда свой ключ — некая struct*/, double>
т.е. значение всегда ВСТРОЕННЫЙ тип
Я пытался присобачить std::remove_if, насколько я понимаю std::remove_if переупорядочивает элементы по значению переданного ему предиката, и возвращает итератор, начиная с которого и до map::end() все элемены подлежат удалению
т.е. по сути надо потом позвать map::erase(returned_iterator, map::end())
что я и делал
НО! косяк был в следующем:
компилятор ругался на отсуствие MyMap::value_type::operator= т.е. он явно пытается какие то элементы переставить местами, и ругается что не может сделать присвоение для пары (MyMap::value_type это на самом деле pair<KEY, VALUE>)
причем для ключа я определил оператор присвоения, а значения в карте у меня встроенные...
В общем поскольку на тот момент было два дня до канадской границы, я действительно просто обходил map и перекладывал из него все что мне надо во временный map, а потом просто присваивал его.
Отсюда вопросы:
1) std::remove_if не умеет и не должен что ли работать с сортированными последовательностями (вроде как да, поскольку славные его перестановки должны видимо нарушать порядок в std::map)
2) Если все же они не должны работать, то как то очень подозрительно отсутствие встроенного алгоритма для именно std::map (в принципе тот же find же реализован в std::map, и логично было ожидать что если общий алгоритм имеет недостатки по сравнению с методом, то метод скорее будет реализован в контейнере). А отсуствие такого метода меня как-то наводило на мысль что все же std::remove_if можно научить работать с map!?!
3) и честно говоря так и не получилось у меня определить map::value_type::operator=
Что думаете коллеги?
0)
> компилятор ругался на отсуствие MyMap::value_type::operator= т.е. он явно пытается какие то элементы переставить местами, и ругается что не может сделать присвоение для пары (MyMap::value_type это на самом деле pair<KEY, VALUE>)
Потому что на самом деле в std::map хранятся std::pair<const Key, Value> — и это правильно (*)
> Отсюда вопросы: > 1) std::remove_if не умеет и не должен что ли работать с сортированными последовательностями (вроде как да, поскольку славные его перестановки должны видимо нарушать порядок в std::map)
Верно.
> 2) Если все же они не должны работать, то как то очень подозрительно отсутствие встроенного алгоритма для именно std::map (в принципе тот же find же реализован в std::map, и логично было ожидать что если общий алгоритм имеет недостатки по сравнению с методом, то метод скорее будет реализован в контейнере). А отсуствие такого метода меня как-то наводило на мысль что все же std::remove_if можно научить работать с map!?!
Т.к. более эффективно, чем простой обход всех значений std::map, реализовать эту специализацию не выйдет, то смысла в ней не видно...
> 3) и честно говоря так и не получилось у меня определить map::value_type::operator=
goto 0)
Posted via RSDN NNTP Server 1.9 gamma
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Юнусов Булат,
> ПК>У стандартного std::map<>::erase возвращаемое значение — void.
> Вроде поднимался вопрос, чтобы переделать чтобы было как у всех контейнеров. И вроде как идею не зарубили совсем.
Насколько я помню последние обсуждения, то как раз наоборот: было решено этого не делать, т.к. для std::map/std::set инкремент итератора в некоторых случаях может оказываться достаточно дорогой операцией.
Posted via RSDN NNTP Server 1.9 gamma
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Wind, Вы писали:
W>у меня есть std::map <int, СЛОЖНАЯ_СТРУКТУРА> a. Периодически возникает задача удаления не нужных записей, причем таких записей может быть несколько и расположены они могут быть в произвольных местах. Алгоритм, который мне пришел в голову: W>1) Перебираю каждый элемент списка на соответствие некоторым условиям W>2) Встретился элемент, который нужно удалить — удалил его и продолжил искать далее
W>Но вот незадача, после удаления элемента итераторы становятся не действительными, значит их нужно задать заново и с элемента .begin() заново начать поиск (заново, т.к. ни кто гарантий не дает, как будут располагаться элементы. Или я не прав?) Это потенциально долго, а на ключи полагаться нельзя, т.к. в момент работы алгоритма значения ключей не известны.
W>Вот такая ерунда. W>Что псоветуете?
А так ?
std::map <int,СЛОЖНАЯ_СТРУКТУРА>::iterator e;
e = std::remove(a.begin(),a.end(),some_map_element); ///если на совпадение
/// e = std::remove_if(a.begin(),a.end(),predicate(...)); ///если по условию
a.erase(e,a.end());
:shuffle:
С уважением Denys Valchuk
IMHO чем больше мнений тем оптимальней выбор варианта... :)
Здравствуйте, Павел Кузнецов, Вы писали:
>> ПК>У стандартного std::map<>::erase возвращаемое значение — void.
>> Вроде поднимался вопрос, чтобы переделать чтобы было как у всех контейнеров. И вроде как идею не зарубили совсем.
ПК>Насколько я помню последние обсуждения, то как раз наоборот: было решено этого не делать, т.к. для std::map/std::set инкремент итератора в некоторых случаях может оказываться достаточно дорогой операцией.
А это не спасёт: добыча итератора на следующий за удалённым элемент в дереве — такая же дорогостоящая, как и простой инкремент. Ну может быть, чуть-чуть дешевле, если после перебалансировки расстояние между элементами в дереве сократилось.
Ну а если говорить точно, то время инкремента — O(log N).
Перекуём баги на фичи!
Re: map и удаление элементов.
От:
Аноним
Дата:
03.11.04 09:43
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>for (MyMap::iterator i = m.begin(), e = m.end(); i != e; /* здесь пусто */ ) ПК>{ ПК> if ( . . . ) ПК> m.erase(i++); ПК> else ПК> ++i; ПК>}
А возможно применение данного алгоритма в том случае, когда удаление одного элемента может потянуть за собой удаление еще нескольких ???
К примеру:
iterator i=begin();
while( i != end() )
{
if(i->second->must_die()) {
delete i->second; // В деструкторе могут удаляться еще несколько объектов map'a ( вызывается erase( key ) )
P::erase( i++ );
} else {
++i;
}
}