Zobrazit předchozí téma :: Zobrazit následující téma |
Autor |
Zpráva |
Quiark

Založen: 29. 07. 2007 Příspěvky: 816 Bydliště: Chlívek 401
|
Zaslal: 30. říjen 2008, 16:22:26 Předmět: Odstranění prvků vyhovujících predikátu z STL map |
|
|
Zdravím, dneska jsem narazil na problém s touto "triviální" úlohou. Mám std::map<int, MujObjekt>. Chtěl bych z ní vyhodit všechny záznamy, kde instance mého objektu splňuje jistý predikát. Jak na to ve standardním C++ ?
Jeden přístup by byl asi takovýto:
kód: |
for (map<...>::iterator i = x.begin(); i != x.end(); ) {
if (i->second splňuje podmínku) {
i = x.erase(i->first);
} else {
i++;
}
}
|
Jenže to funguje jen v MS C++, jinde map::erase nevrací nic.
OK, tak jsem našel funkci remove_if v <algorithm>. Jenže ta zas nejde zkompilovat kvůli absenci operator = v std::pair<>. A kdo ví, jestli vůbec bude na map<> fungovat, když je to spíš strom než sekvence.
A nic jiného mě nenapadá, je tu někdo chytřejší? _________________ Mám strach |
|
Návrat nahoru |
|
 |
MD

Založen: 29. 07. 2007 Příspěvky: 437 Bydliště: Praha
|
Zaslal: 30. říjen 2008, 17:20:07 Předmět: |
|
|
Jen tak, co me v rychlosti napada: Nemel bys tam dat tomu erase primo iterator (tedy i) a ne klic? Pak by to melo smazat a vratit iterator na nasledujici prvek.
Edit: Nejsem si vubec jist jestli se to da takhle v cyklu prochazet a mazat. Ten strom se muze pri deletu preorganizovat a pak by ti mohly nektere objekty utect (dostat se pred iterator a ty bys je preskocil). Pokud tohle nekdo vite, tak me to docela zajima. V MSDN u erase samozrejme nic nepisou.
Edit2: MSDN: Note that this return type does not conform to the C++ standard. _________________ - play with objects - www.krkal.org - |
|
Návrat nahoru |
|
 |
Quiark

Založen: 29. 07. 2007 Příspěvky: 816 Bydliště: Chlívek 401
|
|
Návrat nahoru |
|
 |
Marek

Založen: 28. 07. 2007 Příspěvky: 1782 Bydliště: Velká Morava
|
Zaslal: 30. říjen 2008, 17:44:47 Předmět: |
|
|
Linked list se mi tady zdá vhodnější. Jinak ten erase s iterátorem by měl být celkem snadno implementovatelný. Především se mi nelíbí to, že ten tvůj algoritmus má složitost O(nlogn) (erase s klíčem), když by mohl mít jen O(n) (erase s iterátorem). V případě, že parametr je opravdu klíč, pak bys musel udělat find na hledanej prvek s klíčem, udělat ++iterator na další prvek a smazat ten nalezený. Pokud mažeš přímo podle iterátoru, stačí si jen zapamatovat následující prvek. Mazání během procházení tady nevadí, pokud se to udělá opatrně. Kód na požadované erase:
kód: |
template<typename Key, typename T>
typename std::map<Key, T>::iterator map_erase(std::map<Key, T> &m, typename std::map<Key, T>::iterator it)
{
typename std::map<Key, T>::iterator next = it;
++next;
m.erase(it); // O(1)
// m.erase(it->first); // alternativa v O(logn), nicmene zbytecna prace navic
return next;
}
|
Pozn.: Když se podíváš na implementaci funkci map::erase v MS STL, najdeš tam přesně tohle. _________________ AMD Open Source Graphics Driver Developer |
|
Návrat nahoru |
|
 |
Quiark

Založen: 29. 07. 2007 Příspěvky: 816 Bydliště: Chlívek 401
|
Zaslal: 30. říjen 2008, 17:54:57 Předmět: |
|
|
No tu map<> tam asi z nějakého důvodů mám, žejo Tahle operace se dělá jednou za uherský rok, takže mě rychlost tolik netíží. A je pravda, že udělat it + 1 před smazáním mě mohlo napadnout (jinak ano, iterátor mám k dispozici, do toho kódu nahoře jsem to napsal blbě). Pokud map<> používá standardní binární vyhledávací strom s uspořádáním podle <, potom by přeskládání nemělo vadit, protože pořadí prvků je pořád stejné. _________________ Mám strach |
|
Návrat nahoru |
|
 |
MD

Založen: 29. 07. 2007 Příspěvky: 437 Bydliště: Praha
|
Zaslal: 30. říjen 2008, 17:59:59 Předmět: |
|
|
operace plus ++ na iteratoru jde na nejblizsi vyzsi prvek (jsou serazene), je to tak?
Pak by preskakovani prvku bylo nelogicke. _________________ - play with objects - www.krkal.org - |
|
Návrat nahoru |
|
 |
Tringi

Založen: 28. 07. 2007 Příspěvky: 290
|
Zaslal: 30. říjen 2008, 18:36:28 Předmět: |
|
|
Z dokumentace:
Standard C++ 23.1.4.1, odstavec 8 napsal: |
The insert members shall not affect the validity of iterators and references to the container, and the erase members shall invalidate only iterators and references to the erased elements. |
Dokumentace SGI STL napsal: |
Map has the important property that inserting a new element into a map does not invalidate iterators that point to existing elements. Erasing an element from a map also does not invalidate any iterators, except, of course, for iterators that actually point to the element that is being erased. |
To znamená, klidně to nech takto:
kód: |
for (map<...>::iterator i = x.begin(); i != x.end(); ) {
if (i->second splňuje podmínku) {
x.erase(i++);
} else
++i;
} |
_________________ WWW | GitHub | TW |
|
Návrat nahoru |
|
 |
Quiark

Založen: 29. 07. 2007 Příspěvky: 816 Bydliště: Chlívek 401
|
Zaslal: 30. říjen 2008, 19:08:59 Předmět: |
|
|
Super  _________________ Mám strach |
|
Návrat nahoru |
|
 |
|