2011-10-06 9 views
7

Per quanto riguarda l'algoritmo, la rimozione di un set di elementi da un array contiguo può essere eseguita in modo efficace in due parti.C'è un motivo per usare `remove` al di fuori dell'idioma di cancellazione-rimozione?

  1. Sposta tutti gli elementi da non eliminare nella parte anteriore dell'array.
  2. Contrassegna la matrice più piccola.

Questo può essere fatto in C++ con l'idioma di cancellazione-rimozione.

vector<T> v; // v = {0,1,2,3,0,0,7}; 
vector<T>::iterator it = remove(v.begin(),v.end(),e); 
// move all elements not to be deleted to the front 
// Yes, remove is not the brightest name for that. 
// Especially as list::remove really remove elements from the list. 
// now v = {1,2,3,7,?,?,?} results marked in question marks 
// are implementation dependent. 
v.erase(it,v.end()); 
// get rid of the elements marked as question marks. 
// v = {1,2,3,7} 

Ora, il contenuto degli elementi nel punto interrogativo è sconosciuto. L'unica cosa che possiamo fare con loro è eliminarli (sovrascrivendoli o rimuovendoli).

Esiste una situazione reale in cui è necessario utilizzare remove senza cancellarlo? L'unica situazione che potrei pensare è

copy(src.begin(),src.end(),remove(v.begin(),v.end(),e),v.end()); 

sostituendo tutti A s con B s, e richiedendo che tutti i nuovi B s saranno contigue. Non ha molto senso.

modificare: Ha senso a qualcosa di diverso contenitore di memoria contigious (deque e vector in realtà)?

Se davvero io sono corretto, perché è stato implementato come un algoritmo autonomo, invece di vector::remove_if, dequeue::remove_if ecc

+0

@MatthieuM: Non proprio. Sono * non specificati * in realtà. Vedi questo: http://stackoverflow.com/questions/6456870/stl-remove-doesnt-work-as-expected/6456967#6456967 – Nawaz

risposta

6

La sua domanda va alla rovescia. Piuttosto, si potrebbe chiedere "perché dovrei implementare questo algoritmo identico ripetitivo per ogni contenitore, invece di renderlo una singola funzione libera"?

L'idea chiave per separare contenitori, iteratori e algoritmi è che non si ha un'esplosione di complessità mentre si scrivono più algoritmi e più contenitori. Se si desidera scrivere un nuovo contenitore con spazio di archiviazione contiguo, è possibile utilizzare remove direttamente dalla confezione (a condizione che si forniscano iteratori di inoltro o ad accesso casuale), senza duplicare il codice.

L'unica ragione che certi contenitori implementano le loro versioni utente di algoritmi è perché o possono farlo meglio della versione generica (es std::set::find vs. std::find oppure list::remove), o perché possono fare ciò che la versione generica non può fare (es. std::list::sort vs. std::sort). Ma se puoi, dovresti usare la versione gratuita per la massima generalità e genericità.

+0

* a patto che tu fornisca iteratori ad accesso casuale * 'remove()' non richiede casuale accesso iteratore. – GreenScape

+1

Non vedo come avere una funzione 'list <> :: remove()' dovrebbe comportare l'eliminazione del codice comunque. Sarebbe perfetto se i contenitori fornissero metodi di convenienza come 'vector <> :: for_each()' che sono implementati in termini di 'std :: for_each()' e altri algoritmi standard a meno che l'implementazione non possa fare di meglio. Ridurrebbe sicuramente un sacco di battitura per il caso comune. –

+0

@Kerrek, non sono contrario ad avere _Remove_if internamente. Ma non vedo (1) chi ne ha bisogno a parte il contenitore vettoriale, (2) Perché la tua API lascia più lavoro all'utente? Come ha detto Josh Bloch "Non fare fare al client qualcosa che la biblioteca potrebbe fare http://goo.gl/FjFAv". Qualcuno dovrà scrivere l'idioma rimuovi-cancella per i vettori. E questo qualcuno dovrebbe essere l'API una volta, e non l'utente 100 volte. Puoi lasciare le funzioni 'remove' per implementare strutture vettoriali, ma aggiungi' vector :: remove'! –

0

Se, per qualche motivo, non si desidera ridimensionare il contenitore, ma rimuovere solo alcuni elementi e successivamente impostare altri valori per sostituire lo spazio rimosso, è possibile utilizzare solo rimuovere.

Come ho capito, il valore degli elementi rimossi non è specificato dopo la rimozione, quindi non è possibile utilizzare i loro dati. Sono in qualche modo rimossi, ma lo spazio non è liberato.

+0

È possibile cancellare in modo sicuro elementi dal vettore senza ridimensionarlo. http://stackoverflow.com/questions/5496692/how-to-remove-elements-from-stdvector-without-resizing-it –

+0

@ElazarLeibovich: potrebbe essere ancora più efficiente assegnare una copia piuttosto che distruggere un elemento esistente e quindi copia-costruisci un oggetto nello stesso posto. –

+0

@ AndréCaron, sono d'accordo che potrebbe essere necessario in alcuni casi molto rari, anche se è uno stile orribile. Ma in generale, avere oggetti accessibili che si trovano in uno stato sconosciuto, proprio per non dover chiamare il loro distruttore, sembra una cattiva idea. Quindi credo che forzare l'utente ad implementare la sua "rimozione" in quei casi estremamente rari, non è poi così male. –

4

Prima di tutto: l'implementazione effettiva di remove_if come metodo dei membri avrebbe significato duplicare il codice ancora e ancora.Ho sempre considerato che l'errore fosse remove: MarkB ha risposto a questo, in C++ 03 è più efficiente e in C++ 11 è leggermente più efficiente)

Questo è l'obiettivo di progettazione di STL: strutture e algoritmi di dati separati in modo che se si dispone delle strutture N e degli algoritmi M, non si hanno implementazioni degli algoritmi N*M, ma solo N+M che è decisamente più gestibile.

In particolare, la specifica di remove che gli elementi "mancati" hanno un valore non specificato consente l'applicazione delle nuove operazioni C++ 11 move per una rimozione efficiente.

Ora, lo ammetto che il più delle volte, è usato come parte dell'idioma di cancellazione. Nessuno ti impedisce di crearne uno:

template <typename C> 
void erase(C& container, typename C::const_reference r) { 
    container.erase(std::remove(container.begin(), container.end(), r), container.end()); 
} 
+0

'list :: remove' esiste perché può usare la sua conoscenza dell'elenco per scollegare i nodi. –

+0

Infatti, 'list <> :: remove()' può rimuovere elementi senza copiare gli altri elementi. –

+0

Non vedo come avere una funzione 'list <> :: remove()' dovrebbe comportare l'eliminazione del codice comunque. Sarebbe perfetto se i contenitori fornissero metodi di convenienza come 'vector <> :: for_each()' che sono implementati in termini di 'std :: for_each()' e altri algoritmi standard a meno che l'implementazione non possa fare di meglio. Ridurrebbe sicuramente un sacco di battitura per il caso comune. –

0

Sì. prima di tutto remove() è indipendente dal contenitore. Si può usare su 2 iteratori di qualsiasi contenitore e non dovrebbe fare le cose come:

if(<iterator is vector iterator>) 
{ 
    vector::remove_if(...); 
} 
else(<iterator is dequeue iterator>) 
{ 
    dequeue::remove_if(...); 
} 
else(<iterator is list iterator>) 
{ 
    list::remove_if(...); 
} 
// etc 

Sì c'è motivo per non eseguire la erase(). Per il vettore lo rende più piccolo e quindi esegue la ridistribuzione della memoria. Ciò che non è sempre desiderato, perché causerà molti costruttori di copia per altri elementi del vettore.

+0

Puoi usarlo su tutti i contenitori, ma non vedo alcun motivo per cui tu voglia usalo su qualsiasi cosa eccetto 'vector' o' deque'. Vedi il link qui sotto, cancella non ridimensiona il tuo vettore. http://stackoverflow.com/questions/5496692/how-to-remove-elements-from-stdvector-without-resizing-it –

+0

@Elazar Leibovich hai torto, signore. http://www.cplusplus.com/reference/stl/vector/erase/ * Questo riduce efficacemente la dimensione del vettore per il numero di elementi rimossi, chiamando prima il distruttore di ogni elemento. * Finché i dati nel vettore sono contigui non c'è modo di distruggere/costruire l'oggetto senza liberare/allocare memoria. A meno che non vengano utilizzati nuovi/cancelli. Dubito fortemente dell'uso degli ultimi. – GreenScape

+0

Umm .. Penso che sia possibile chiamare Ctor/Dtor senza allocazione di memoria, con posizionamento nuovo/cancella. –

1

Sì. Un semplice contro-esempio:

void bar { 
    // Get vector of T's from foo() 
    vector<T> v = foo(); 
    // Print only non-zero elements. 
    vector<T>::iterator it = remove(v.begin(),v.end(), T(0)); 
    // I could call erase here, but why bother? 
    std::copy(v.begin(), it, std::ostream_iterator<T>(std::cout)); 
} 

Se ho intenzione di usare solo la parte selezionata del vettore, e non hanno altri usi per il vettore, non ho bisogno di erase il vettore. Tutti gli elementi saranno comunque distrutti quando il vettore esce dal campo di applicazione.

+0

Non è necessario cancellare il vettore, ma cancellarlo è un'idea eccellente. Dal momento che il prossimo programmatore potrebbe usare quelle variabili sconosciute dopo la tua copia per qualche motivo. Il prezzo della distruzione di quegli elementi sembra trascurabile nel 99% dei casi e avere oggetti accessibili in uno stato non specificato è un grande prezzo da pagare per questo. –

+1

E BTW, non vedo cosa guadagni non cancellando quegli elementi. Stai facendo un lavoro identico (in entrambi i casi tutti gli elementi sarebbero stati distrutti), ma in un modo più strano e incline agli errori. –

Problemi correlati