2012-03-05 3 views
5

vector에서 일부 요소를 제거하고이를 remove_if 알고리즘을 사용하여 제거하려고합니다. 하지만 제거 된 요소를 추적하여 나중에 일부 작업을 수행 할 수있게하려고합니다.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; 
} 

을하지만 remove_if내 펑터 오브젝트의 복사 허용, 그래서 저장된 evens 벡터에 액세스 할 수없는 것처럼이 작동하지 않습니다 : 나는 다음과 같은 코드로이 시도. 이것을 성취하는 올바른 방법은 무엇입니까?

P. 예를 들어 짝수와 홀수는 예를 들어 술일뿐입니다. 제 실제 코드는 약간 다릅니다. 따라서 짝수 또는 홀수를 다르게 식별하는 방법을 제안하지 마십시오.

+0

참조로 펑터를 전달할 수 있습니다. 'boost' 또는'C++ 11' ('ref'를 가진 패스)을 사용하여 솔루션을 사용할 수 있습니까? – nabulke

+0

다음과 같이 할 수 있습니다'for (vector :: iterator it = newEnd; it! = v.end(); ++ it) { cout << * it << "\ n"; } – megabyte1024

+0

@ megabyte1024 : 아니요 작동하지 않습니다. } 'evens'를 포함하지 않고 올바른 결과를 얻으려면 v.erase (newEnd, v.end()); newEnd를 넘어서는 요소는 제거 된 요소가 될 수 없다. – Asha

답변

9

이 솔루션은 remove_if이 아니지만, 사촌 partial_sort partition입니다. 차이점은 remove_if[begin, middle)에 일치하는 요소가 포함되어 있음을 보장하지만 partition은 또한 [middle, end)에 조건 자와 일치하지 않는 요소가 있음을 보장합니다.

그래서, 당신의 예는 ( evens가 더 이상 필요하지 유의하지 않음)된다 :

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'는 무엇을 반환합니까? 내 시스템에서는 컴파일되지 않습니다. – Asha

+0

어, 내가 어떻게 그걸 꺼 냈어? 고침 ... 십자가에 읽는 것 같아요. – MSalters

+0

고마워...... 이걸 모르겠다. 그냥 내 조건을 바꿨다. 잘 작동한다. .. – Asha

0

코드에서 볼 수있는 문제는 remove_if 알고리즘이 호출 할 때마다 struct 내부에 생성 한 evens 벡터가 생성된다는 것입니다. 따라서 펑터를 remove_if에 전달하더라도 매번 새로운 벡터가 생성됩니다. 따라서 마지막 요소가 제거되고 함수 호출이 끝나고 함수에서 빠지면 f.evens는 항상 빈 벡터를 가져옵니다. 이것은

  1. 는 클래스와 구조체를 교체하고 (즉, 당신이 원하는 어떤 경우)
  2. 정적으로 고르게를 선언 또는 당신은 고르게 글로벌 만들 수있는 두 가지 방법으로 분류 할 수있다. 개인적으로 추천하지는 않을 것입니다 (코드를 잘못 작성하고, 정말로 필요하지 않으면 전역에 적용하지 마십시오).

편집 : 제안으로

nabulke 당신이 표준 : 심판이 뭔가도 할 수 likke이, 표준 : REF (F). 이렇게하면 벡터를 전역으로 만들지 않고 불필요한 통계를 피할 수 있습니다. 다음과 같이

는 글로벌 만드는 샘플은

#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; 
} 

이 코드는 나를 위해 잘 작동하는 것 같다입니다. 이것이 당신이 원하는 것이 아니라면 알려주십시오.

+0

'std :: ref' 도움말과 함께 펑터를 전달하지 않겠습니까? – nabulke

+0

@nabulke : Hhhhmmm 그렇게 생각합니다. 가능한 솔루션에 추가 될 수 있습니다. – Ajai

1

추가 수준의 간접 참조. 벡터를 로컬에서 선언하고 에 IsEven에 복사본이 포함되어 있습니다. 벡터가 a shared_ptr에 의해 동적으로 할당되고 관리되는 경우 IsEven에서 까지 벡터를 소유 할 수도 있습니다. 실제로, 나는 일반적으로 지역 변수 에 더한 포인터 솔루션을 더 편리하게 발견했다. 같은 뭔가 다음 operator()() 기능 const을 할

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; 
    } 
} 

참고이 또한 수 있다는 점. 나는 이 형식적으로 필요하다고 생각합니다 (확실하지는 않지만).

+0

참조가 아닌 포인터를 사용하는 경우 +1합니다. 그리고 어쨌든 벡터를 생성해야 할 때 아마 plain-old-data에 두어 이니셜 라이저로 로컬 변수를 초기화 할 것입니다. –

+0

@JanHudec 참조를 사용하는 경우 정의되지 않은 동작입니다. 기능 객체는 CopyAssignable이어야합니다. 그리고 그것을 POD로 남겨두면 임시 구조를 만들 수 없다는 것을 의미합니다. 사용 패턴은 벡터를 선언 한 다음'remove_if '의 인수로'IsEven (& v)'를 사용하는 것입니다. –

+0

흠, 나는 CopyConstructible이 아니라 Assignable이어야한다는 명확한 진술을 찾을 수 없기 때문에 지금은 혼란 스럽다. CopyConstructible은 분명히 참조 할 수 있지만 Assignable은 참조 할 수 없습니다. –

2

당신은 당신의 펑 복사를 방지 할 수 있습니다 (즉,이 값에 의해 통과)이 같은 참조에 의해 인도 표준시 전달하는 경우 :

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

당신이 boost을 사용할 수없는 경우

, 동일 std::ref 가능합니다. 위 코드를 테스트 한 결과 예상대로 작동합니다.

+1

+1.하지만 boost :: ref는 std :: ref의 전신이다. 그것은 부스트에서 TR1로 만들었던 것보다 처음으로 도입되었습니다 (많은 컴파일러가'std :: tr1 :: ref'로 그것을 가지고 있으며 결국 C++ 11에서 표준화되었습니다). –

3

가장 좋은 건 std::partition()입니다. 프리디 케이트 리턴 true이 false를 반환하는 모든 엘리트와 같이 시퀀스의 모든 엘리트를 재 배열합니다.

Exemple : 당신은 다른 해결책이있을 수 있습니다

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

; 같은 시간에 엘리트를 제거 할 필요가없는 경우에만 가능합니다 (그렇습니까?). functor의 복사본을 반환하는 std::for_each(). 예 :

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")); 

가능한 경우 C++에서 이름이없는 변수를 만드는 것이 좋습니다. 여기에 그 해결책이 주 문제 (소스 컨테이너에서 엘리트 제거)에 정확히 답하지는 않지만 모든 사람에게 std :: for_each()가 함수기의 복사본을 반환한다는 것을 상기시켜줍니다. :-)