2012-03-05 11 views
5

Voglio rimuovere alcuni elementi da un vector e sto usando l'algoritmo remove_if per fare questo. Ma voglio tenere traccia degli elementi rimossi in modo da poter eseguire qualche operazione su di essi in seguito. Ho provato questo con il seguente codice:Tenere traccia degli elementi rimossi usando std :: remove_if

#include <vector> 
#include <algorithm> 
#include <iostream> 

using namespace std; 


struct IsEven 
{ 
    bool operator()(int n) 
    { 
     if(n % 2 == 0) 
     { 
      evens.push_back(n); 
      return true; 
     } 

     return false; 
    } 

    vector<int> evens; 
}; 

int main(int argc, char **argv) 
{ 

    vector<int> v; 
    for(int i = 0; i < 10; ++i) 
    { 
     v.push_back(i); 
    } 

    IsEven f; 
    vector<int>::iterator newEnd = remove_if(v.begin(), v.end(), f); 
    for(vector<int>::iterator it = f.evens.begin(); it != f.evens.end(); ++it) 
    { 
     cout<<*it<<"\n"; 
    } 

    v.erase(newEnd, v.end()); 

    return 0; 
} 

Ma questo non funziona come remove_if accetta l'copia del mio oggetto funtore, in modo che la stored evens vettore non è accessibile. Qual è il modo corretto per raggiungere questo obiettivo?

P.S. : L'esempio, con odds e odds è solo per esempio, il mio vero codice è qualcosa di diverso. Quindi non suggerire un modo per identificare in modo uguale o dispari.

+0

Potresti passare il tuo funtore per riferimento. Una soluzione è accettabile usando 'boost' o' C++ 11' (per passare con 'ref')? – nabulke

+0

Si può fare ciò che segue 'for (vector :: iterator it = newEnd; it! = V.end(); ++ it) { cout << * it <<" \ n "; } v.erase (newEnd, v.end()); 'per ottenere risultati corretti senza coinvolgere' evens' – megabyte1024

+0

@ megabyte1024: No, non funziona. Gli elementi oltre 'newEnd' non sono garantiti come elementi rimossi. – Asha

risposta

9

La soluzione non è remove_if, ma è il cugino partial_sort partition. La differenza è che remove_if garantisce solo che [begin, middle) contenga gli elementi corrispondenti, ma partition garantisce anche che [middle, end) contenga gli elementi che non corrispondono al predicato.

Così, il vostro esempio diventa solo (si noti che evens non è più necessaria):

vector<int>::iterator newEnd = partition(v.begin(), v.end(), f); 
for(vector<int>::iterator it = newEnd; it != v.end(); ++it) 
{ 
    cout<<*it<<"\n"; 
} 
v.erase(newEnd, v.end()); 
+0

'partial_sort' restituisce qualcosa? Questo non si sta compilando sul mio sistema. – Asha

+0

Eh, come ho fatto a staccarlo? Fissaggio ... Penso che la lettura strabica. – MSalters

+0

Grazie..non lo sapevo .. ho semplicemente invertito la mia condizione di predicato e sta funzionando bene ora .. – Asha

0

Il problema che vedo con il codice è che il vettore evens che si crea all'interno della struct viene creato ogni volta che l'algoritmo remove_if lo chiama. Quindi non importa se si passa in un functor per rimuovere_if creerà un nuovo vettore ogni volta. Quindi una volta rimosso l'ultimo elemento e quando la chiamata alla funzione termina e esce dalla funzione, f.evens recupera sempre un vettore vuoto. Questo potrebbe essere ordinato in due modi,

  1. Sostituire la struct con una classe e dichiarare eveni come statica (se questo è ciò che si voleva)
  2. Oppure si potrebbe fare Evens globale. Non lo consiglierei personalmente (rende il codice sbagliato, dite di no ai globali a meno che non ne abbiate veramente bisogno).

Edit:

Come suggerito da nabulke si potrebbe anche std :: Rif qualcosa likke questo, std :: ref (f). Ciò ti impedisce di rendere globale il vettore ed evita statiche inutili.

Un campione di rendere globale è la seguente,

#include <vector> 
#include <algorithm> 
#include <iostream> 

using namespace std; 
vector<int> evens; 

struct IsEven 
{ 
    bool operator()(int n) 
    { 
     if(n % 2 == 0) 
     { 
      evens.push_back(n); 
      return true; 
     } 

     return false; 
    } 


}; 

int main(int argc, char **argv) 
{ 

    vector<int> v; 
    for(int i = 0; i < 10; ++i) 
    { 
     v.push_back(i); 
    } 

    IsEven f; 
    vector<int>::iterator newEnd = remove_if(v.begin(), v.end(), f); 
    for(vector<int>::iterator it = evens.begin(); it != evens.end(); ++it) 
    { 
     cout<<*it<<"\n"; 
    } 

    v.erase(newEnd, v.end()); 

    return 0; 
} 

Questo codice sembra funzionare bene per me. Fammi sapere se questo non è quello che volevi.

+0

Non passare il functor con l'aiuto 'std :: ref'? – nabulke

+0

@nabulke: Hhhhmmm credo di si. Ciò potrebbe aggiungere alla possibile soluzione. – Ajai

1

Un ulteriore livello di riferimento indiretto. Dichiarare il vettore localmente e avere IsEven contenere una copia. È inoltre possibile che IsEven a sia proprietario del vettore, a condizione che sia assegnato e gestito dinamicamente da a shared_ptr. In pratica, ho trovato generalmente più conveniente la variabile locale più la soluzione puntatore. Qualcosa di simile:

class IsEven 
{ 
    std::vector<int>* myEliminated; 
public: 
    IsEven(std::vector<int>* eliminated = NULL) 
     : myEliminated(eliminated) 
    { 
    } 
    bool 
    operator()(int n) const 
    { 
     bool results = n % 2 == 0; 
     if (results && myEliminated != NULL) { 
      myEliminated->push_back(n); 
     } 
     return results; 
    } 
} 

Si noti che questo permette anche la funzione operator()() di essere const. I penso che questo sia richiesto formalmente (anche se non ne sono sicuro).

+0

Darei +1 se stava usando il riferimento e non il puntatore. E probabilmente lo lascerei a plain-old-data e inizializzerò la variabile locale con l'inizializzatore, quando dovrò comunque crearne uno per il vettore. –

+0

@JanHudec È un comportamento non definito se utilizza un riferimento; gli oggetti funzionali devono essere CopyAssignable. E lasciare POD significa che non si può costruire un temporaneo: il mio modello di utilizzo sarebbe quello di dichiarare il vettore, quindi usare 'IsEven (& v)' come argomento per 'remove_if'. –

+0

Hm, non riesco a trovare una dichiarazione definitiva che debba essere Assignable e non solo CopyConstructible, quindi sono un po 'confuso ora. Il riferimento è ovviamente CopyConstructible, ma non Assignable. –

2

È possibile evitare di copiare il tuo funtore (cioè passaggio per valore) se si passa ist da riferimento in questo modo:

vector<int>::iterator newEnd = remove_if(v.begin(), v.end(), 
    boost::bind<int>(boost::ref(f), _1)); 

Se non è possibile utilizzare boost, lo stesso è possibile con std::ref. Ho testato il codice sopra e funziona come previsto.

+1

+1, ma si noti che 'boost :: ref' è il predecessore di' std :: ref'. È stato introdotto per la prima volta in boost, poi è passato a TR1 (così tanti compilatori lo hanno come 'std :: tr1 :: ref' ed infine è stato standardizzato in C++ 11. –

3

La vostra migliore scommessa è std::partition() che riorganizzerà tutti i membri della sequenza come tutti i membri per i quali il vostro ritorno di riferimento true precederà quelli per cui restituisce false.

Esempio:

vector<int>::iterator bound = partition (v.begin(), v.end(), IsEven); 
std::cout << "Even numbers:" << std::endl; 
for (vector<int>::iterator it = v.begin(); it != bound; ++it) 
    std::cout << *it << " "; 

std::cout << "Odd numbers:" << std::endl; 
for (vector<int>::iterator it = bound; it != v.end(); ++it) 
    std::cout << *it << " "; 
0

Potrebbe essere un'altra soluzione; solo se non hai bisogno di rimuovere elts nello stesso tempo (vero?). Con std::for_each() che restituisce una copia del tuo functor. Esempio:

IsEven result = std::for_each(v.begin(), v.end(), IsEven()); 

// Display the even numbers. 
std::copy(result.evens.begin(), result.evens.end(), std::ostream_iterator<int> (cout, "\n")); 

Si precisa che è sempre meglio per creare le variabili senza nome in C++, quando possibile. Qui quella soluzione non risponde esattamente al tuo problema principale (rimuovendo elts dal contenitore di origine), ma ricorda a tutti che std :: for_each() restituisce una copia del tuo functor. :-)

Problemi correlati