2013-06-04 8 views
7

Ich möchte Elemente aus einem Vektor mit remove_if Funktion entfernen, aber das Löschen auf N Elemente beschränken.Übergeben Sie zusätzliche Argumente zu remove_if

Beispiel:

// predicate function that determines if a value is an odd number. 
bool IsOdd (int i) { 

    if (we deleted more than deleteLimit) 
    return false; 

    return ((i%2)==1); 
} 


void otherFunc(){ 
    int deleteLimit = 10; 

    // remove odd numbers:      
    std::vector<int>::iterator newEnd = 
    std::remove_if (myints.begin(), myints.end(), IsOdd (how to pass deleteLimit?)); 
} 

Ich brauche das IsOdd Prädikat speichert, wie viele Elemente es entfernt und wie viele wir wollen löschen. Die einzige Möglichkeit besteht darin, eine globale Variable zu verwenden? Wie folgt aus:

int deleteLimit = 10; 
int removedSoFar = 0; 
bool IsOdd (int i) { 

    if (deleteLimit < removedSoFar) 
    return false; 

    if (i%2==1) { 
    removedSoFar++ 
    return true; 
    } 

    return false; 

} 
remove_if ... 
+0

Sie können auch verwenden myints.size() in einigen Funktors oder Lambda-Funktion, um zu bestimmen, wie viele Elemente gelöscht wurden. – Kamouth

+1

Ich bin nicht sicher, dass Prädikate, die an 'remove_if' übergeben werden, statusbehaftet sein dürfen –

+0

@Kamouth Aber' std :: remove_if' entfernt keine Elemente, sondern verschiebt sie nur bis zum Ende und Sie sollten dann 'Erase' verwenden entfernen Sie sie tatsächlich aus dem Container. – leemes

Antwort

11

Der Status "Wie viele Elemente wurden bisher entfernt" sollte außerhalb der Funktion/des Aufrufs des Algorithmus definiert werden. Dies liegt daran, dass ein Funktor keinen Zustand haben sollte, der beim Aufruf geändert wird (dies wäre ein undefiniertes Verhalten).

Sie sollten einen Verweis auf diesen Status (Zähler) im Konstruktor des Funktors nehmen (oder durch Referenz im Lambda erfassen), so dass Sie auf diesen Zähler zugreifen und diesen ändern können. Wenn dieser Funktor jetzt kopiert wird, ist es egal, welcher vom Algorithmus aufgerufen wird, da alle von ihnen jetzt den Bezug auf den gleichen Zustand haben.

Mit functors (C++ 03):

class IsOdd { 
    int deleteLimit; 
    int & deletedSoFar; 

public: 
    IsOdd(int deleteLimit, int & deletedSoFar) : 
     deleteLimit(deleteLimit), deletedSoFar(deletedSoFar) 
    {} 

    bool operator()(int i) const { 
     if (deletedSoFar < deleteLimit && i % 2) { 
      ++deletedSoFar; 
      return true; 
     } 
     return false; 
    } 
}; 

int deletedSoFar = 0; 
int deleteLimit = 10; 
std::remove_if (myints.begin(), myints.end(), IsOdd(deleteLimit, deletedSoFar)); 

Mit Lambda (C++ 11):

int deletedSoFar = 0; 
int deleteLimit = 10; 
auto it = std::remove_if (myints.begin(), myints.end(), [deleteLimit,&deletedSoFar](int i){ 
    if (deletedSoFar < deleteLimit && i % 2) { 
     ++deletedSoFar; 
     return true; 
    } 
    return false; 
}); 
myints.erase(it, myints.end()); 
+0

Ja, das Lambda-Beispiel ist das, das ich verwenden werde. Ähnlich wie ForEvers Antwort, aber er weigerte sich, deletedSoFar als Argument hinzuzufügen. – dynamic

+1

Es ist erwähnenswert, dass OP wahrscheinlich löscht/remove_if Idiom, nicht nur Remove_if Anruf benötigt. –

+0

@leemes Ich habe deine Antwort bearbeitet. Könnten Sie nach Möglichkeit überprüfen, ob es sich um eine gültige Bearbeitung handelt? Wenn nicht, meine Entschuldigung und bitte machen Sie einen Rollback. – streppel

2

Verwenden functor, Struktur mit operator() oder benutze binden Features (std :: bind/boost :: bind).

bool isOdd(int current, int limit, int& count) 
{ 
    if (count >= limit) 
    { 
     return false; 
    } 
    if (current % 2 == 1) 
    { 
     ++count; 
     return true; 
    } 
    return false; 
} 

int count = 0, limit = 10; 
vec.erase(std::remove_if(vec.begin(), vec.end(), 
std::bind(&isOdd, _1, limit, std::ref(count)), vec.end()); 

Und mit Funktors

struct IsOdd : public std::unary_function<int, bool> 
{ 
public: 
    IsOdd(int l) : count(0), limit(l) 
    { 
    } 
    bool operator() (int i) 
    { 
     if (count >= limit) 
     { 
     return false; 
     } 
     if (current % 2 == 1) 
     { 
     ++count; 
     return true; 
     } 
     return false; 
private: 
    int count; 
    int limit; 
}; 

int limit = 10; 
vec.erase(std::remove_if(vec.begin(), vec.end(), 
isOdd(limit)), vec.end()); 
+0

Sie haben Ihre 'operator()' const markiert, aber sie ändert 'count'.Es sollte nicht-const sein, aber das bedeutet, dass das Kopieren des Funktors innerhalb des Algorithmus gefährlich ist. Es wäre besser, wenn Sie einen Zähler per Referenz im Funktor nehmen und diesen stattdessen ändern. Ihr Operator kann dann immer noch const sein, wenn ich nicht falsch liege. – leemes

4

Neben Ihren eigenen Funktor erstellen, können Sie einen Lambda-Ausdruck übergeben:

auto deleteLimit = 25; 
auto removedSoFar = 0; 
auto it = remove_if (myints.begin(), 
        myints.end(), 
        [deleteLimit, &removedSoFar](int i)->bool 
        { 
         if ((deletedSoFar < deleteLimit) && (i % 2)) { 
          ++deletedSoFar; 
          return true; 
         } 
         return false; 
         }); 

// really remove the elements from the container 
myints.erase(it, myints.end()); 

jedoch aufpassen, dass stateful functors and std library algorithms are not always good mix. Hier können Aufrufe an den Lambda einen Nebeneffekt haben, also haben Sie keine Garantie über welche Elemente in der Sequenz entfernt werden.

Beachten Sie den letzten Anruf an std::vector::erase. Dies ist erforderlich, um die unerwünschten Elemente wirklich aus dem Container zu entfernen. Siehe erase remove idiom.

+0

um die Zählung zu behalten? etwas wie '[deleteLimit & removedSoFar] ...'? Behält RemovedSoFar die Werte nach jeder Iteration bei? Auch in Ihrem Beispiel wird der Wert nicht erhöht. – dynamic

+0

@ yes123 Prinzipiell ja, um die Zählung zu behalten, durch Referenz erfassen. Beachten Sie jedoch, dass das Aufrufen von Algorithmen wie "std :: remove_if" mit "stateful" Funktoren keine gute Idee ist. – juanchopanza

+0

Also ist weder Ihr Beispiel eine gute Idee? – dynamic

2
int initial_size = myints.size(); 
std::remove_if (myints.begin(), myints.end(), [myints.size(),limit,initial_size](int i)->bool{ 
return ((initial_size-myints.size())<limit ? i%2 : false) }); 
2

Der Code in der Antwort gestimmt up hat eine kleine Fehler. Es fehlen Klammern vor dem Operator-Schlüsselwort. Ich konnte die Bearbeitung nicht mit weniger als sechs Zeichen durchführen und konnte nicht kommentieren, da meine Punktzahl zu niedrig ist.

class IsOdd { 
     int deleteLimit; 
     int & deletedSoFar; 

    public: 
     IsOdd(int deleteLimit, int & deletedSoFar) : 
      deleteLimit(deleteLimit), deletedSoFar(deletedSoFar) 
     {} 

     bool operator()(int i) const { 
      if (deletedSoFar < deleteLimit && i % 2) { 
       ++deletedSoFar; 
       return true; 
      } 
      return false; 
     } 
    }; 

    int deletedSoFar = 0; 
    int deleteLimit = 10; 
    std::remove_if (myints.begin(), myints.end(), IsOdd(deleteLimit, deletedSoFar)); 
Verwandte Themen