Jak usunąć elementy z kontenerów STL?

Jak wymazać elementy z kontenerów STL, które mają określoną wartość lub spełniają jakiś warunek ?

Czy istnieje jeden wspólny lub jednolity sposób zrobienia tego dla różnych rodzajów kontenerów?

Author: Mr.C64, 2013-04-15

1 answers

Niestety, nie ma ani jednego jednolitego interfejsu ani wzorca do usuwania elementów z kontenerów STL. Pojawiają się jednak trzy zachowania:

Std:: vector Pattern

Aby usunąć elementy, które spełniają określony warunek z std::vector, powszechną techniką jest tzw. erase-Usuń idiom.

Jeśli v jest instancją std::vector i chcemy wymazać elementy o wartości x z wektora, taki kod może być używany:

// Erase elements having value "x" from vector "v"
v.erase( std::remove(v.begin(), v.end(), x), v.end() );
Jeśli kryterium wymazywania elementów jest bardziej złożone niż proste "element do wymazania = = x" , można użyć algorytmu std::remove_if() zamiast std::remove():
// Erase elements matching "erasing_condition" from vector "v"
v.erase( std::remove_if(v.begin(), v.end(), erasing_condition), v.end() );

Gdzie erasing_condition jest jednoznacznym predykatem, który może być wyrażony w kilku formach: np. może być bool-funkcja zwracająca przyjmująca Typ elementu wektorowego jako dane wejściowe (więc jeśli zwracana wartość to true, element zostanie usunięty z wektora; jeśli jest to false, nie); lub może być wyrażona w linii jako lambda; może być functor; itd.

(zarówno std::remove(), jak i std::remove_if() są algorytmami generycznymi z nagłówka <algorithm>.)

Tutaj jest jasne wyjaśnienie Z Wikipedii :

Biblioteka algorithm dostarcza remove i remove_if algorytmy na to. Ponieważ algorytmy te działają w zakresie elementów oznaczonych przez dwa Iteratory, nie mają wiedzy o podstawa pojemnik lub kolekcja. Tak więc żadne elementy nie są w rzeczywistości wyjęty z pojemnika. Raczej wszystkie elementy, które nie pasują do kryteria usuwania są zestawione z przodu zakresu, w ten sam względny porządek. Pozostałe elementy pozostają w poprawnym, ale nieokreślony stan. Po wykonaniu tej czynności remove zwraca iterator / align = "left" /

Aby faktycznie wyeliminować elementy z pojemnika, remove jest połączone z pojemnikiem erase funkcja członka, stąd nazwa "Wymaż-Usuń idiom".

Zasadniczo, std::remove() i std::remove_if() kompaktują elementy, które nie spełniają kryteriów kasowania na początku zakresu (tj. na początku vector), a następnie erase() eliminują pozostałe elementy z pojemnika.

Ten wzór dotyczy również innych kontenerów, takich jak std::deque.

Std:: list Pattern

Aby usunąć elementy z std::list, proste remove() i remove_if() metody są dostępne:

// Erase elements having value "x" from list "l"
l.remove( x )

// Erase elements satisfying "erasing_condition" from list "l"
l.remove_if( erasing_condition );

(gdzie erasing_condition jest jednoznacznym predykatem, o tych samych cechach omówionych dla std::remove_if() w powyższej sekcji.)

Ten sam wzór można zastosować do podobnych pojemników, jak std::forward_list.

Kontenery asocjacyjne (np. std:: map, std:: set, ...) Wzór

kontenery asocjacyjne jak std::map, std::set, std::unordered_map, itd. postępuj zgodnie z opisanym tutaj powszechnym wzorem:

  1. Jeśli warunek kasowania jest prostym dopasowaniem klucza (np. "Wymaż element mając klucz x " ), wówczas prosty erase() metoda może być wywołana:

    // Erase element having key "k" from map "m":
    m.erase( k );
    
  2. Jeśli warunek kasowania jest bardziej złożony i jest wyrażony przez pewien zwyczaj można użyć pętli "wymazać wszystkie nieparzyste elementy"), wtedy można użyć pętli for (z wyraźnym sprawdzaniem stanu kasowania w ciele pętli oraz wywołanie metody erase(iterator)):

    //
    // Erase all elements from associative container "c", satisfying "erasing_condition":
    //
    for (auto it = c.begin(); it != c.end(); /* "it" updated inside loop body */ )
    {
        if ( erasing_condition(*it) )
        {   
            // Erase the element matching the specified condition 
            // from the associative container.
            it = c.erase(it);
    
            // Note:
            // erase() returns an iterator to the element 
            // that follows the last element removed, 
            // so we can continue the "for" loop iteration from that position.
        }
        else
        {
            // Current element does _not_ satisfy erasing condition,
            // so we can just move on to the next element.
            ++it;
        }       
    }     
    

Potrzeba jednolitego podejścia

Jak można zauważyć z powyższej analizy, niestety nie ma jednolitego wspólnego podejścia do usuwania elementów z kontenerów STL.

Poniższa tabela podsumowuje wyżej wymienione wzory:

----------------+------------------------------------------             
   Container    |            Erasing Pattern
----------------+------------------------------------------                
                |
 vector         |    Use erase-remove idiom.
 deque          |
                |
----------------+------------------------------------------               
                |
 list           |    Call remove()/remove_if() methods.
 forward_list   |
                |
----------------+------------------------------------------  
                |
 map            |    Simple erase(key) method call, 
 set            |    or 
 unordered_map  |    loop through the container,
 multimap       |    and call erase(iterator) on matching
                |    condition.
 ...            |
                |
----------------+------------------------------------------

Pisanie specyficznego kodu na podstawie konkretnego kontenera jest podatne na błędy, trudne do utrzymania, trudne do odczytania itp.

Jednak to możliwość zapisu szablonów funkcji o nazwach zwyczajowych (np. erase() i erase_if()) przeciążone dla różnych typów kontenerów i osadzone wyżej wymienione implementacje wzorców w tych funkcjach.
Tak więc, klient może po prostu wywołać te erase() i erase_if() funkcje ogólne, a kompilator wyśle wywołanie do właściwej implementacji (w czasie kompilacji ), W oparciu o Typ kontenera.

Bardziej eleganckim podejściem, wykorzystującym technikę meta-programowania szablonów, jest przedstawione przez Stephan T. Lavavej tutaj.

 54
Author: Mr.C64,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:14