2012-06-14 3 views
2

STL 컨테이너에서 하위 의한 요소를 제거하는 방법 (객체 다른 필드 사이 용어 문자열을 문자열 필드 contai 용어 노드) 어떤 처리 후의I 객체의 벡터가

class TermNode { 
private: 
    std::wstring term; 
    double weight; 
    ... 
public: 
    ... 
}; 

상기 산출 같은

std::vector<TermNode *> termlist; 

이 벡터의 결과 목록 점수가 이러한 개체가 마지막으로 TermNode 포인터의 벡터에 저장된 얻을, 400 개의 항목을 포함하는 다음과 같습니다

DEBUG: 'knowledge' term weight=13.5921 
DEBUG: 'discovery' term weight=12.3437 
DEBUG: 'applications' term weight=11.9476 
DEBUG: 'process' term weight=11.4553 
DEBUG: 'knowledge discovery' term weight=11.4509 
DEBUG: 'information' term weight=10.952 
DEBUG: 'techniques' term weight=10.4139 
DEBUG: 'web' term weight=10.3733 
... 

제가하려는 것은 하위 문자열에 대한 최종 목록도 용어 목록 안의 구에 포함되어 있습니다. 예를 들어 위의 목록 조각을 보면 '지식 검색'이라는 문구가 있으므로 '지식''지식''검색'을 목록에서 제외하고 싶습니다. 이러한 맥락에서 중복된다. 나는 한 마디가 들어있는 구절을 지키고 싶다. 나는 또한 모든 문자열을 같거나 3 자 이하로 제거하려고 생각하고있다. 그러나 그것은 지금 당장의 생각입니다.

이 정리 과정에서는 remove_if/find_if (새로운 C++ lambdas 사용)를 사용하여 클래스를 코딩하고 있으며 해당 클래스를 컴팩트 클래스로 사용하는 것이 좋습니다.

이 문제를 해결하는 방법에 대해서는 잘 모르겠습니다. 문제는 먼저 플래그를 삭제 마커로 설정하여 제거 할 문자열을 먼저 식별해야한다는 것입니다. 그 목록을 사전 처리해야한다는 뜻입니다. 나는 그 단일 용어 중 하나를 포함하고있는 단일 용어와 구문을 찾아야 할 것입니다. 나는 이것이 쉬운 일이 아니며 진보 된 알고리즘이 필요하다고 생각한다. 접미어 트리를 사용하여 하위 문자열을 식별합니까?

벡터에있는 또 다른 루프와 동일한 벡터의 복사본을 정리할 수 있습니다. 나는 가장 효율적인 방법을 찾고있다.

나는 std::list erase incompatible iterator에 나와있는 것과 같은 아이디어 나 방향을 가지고 놀고 있는데, 이는 remove_if/find_if와 Erasing multiple objects from a std::vector?에서 사용 된 아이디어를 사용하고 있습니다.

그래서 기본적으로이 작업을 수행하고 다중 루프를 피하는 스마트 한 방법이 있으며 삭제에 대한 단일 용어를 어떻게 식별 할 수 있습니까? 어쩌면 나는 정말로 뭔가를 놓치고 있을지 모르지만 아마 누군가가 나와서 좋은 힌트를 줄 것입니다.

의견을 보내 주셔서 감사합니다.

/** 
* Functor gets the term of each TermNode object, looks if term string 
* contains spaces (ie. term is a phrase), splits phrase by spaces and finally 
* stores thes term tokens into a set. Only term higher than a score of 
* 'skipAtWeight" are taken tinto account. 
*/ 
struct findPhrasesAndSplitIntoTokens { 
private: 
    set<wstring> tokens; 
    double skipAtWeight; 

public: 
    findPhrasesAndSplitIntoTokens(const double skipAtWeight) 
    : skipAtWeight(skipAtWeight) { 
    } 

    /** 
    * Implements operator() 
    */ 
    void operator()(const TermNode * tn) { 
     // --- skip all terms lower skipAtWeight 
     if (tn->getWeight() < skipAtWeight) 
      return; 

     // --- get term 
     wstring term = tn->getTerm(); 
     // --- iterate over term, check for spaces (if this term is a phrase) 
     for (unsigned int i = 0; i < term.length(); i++) { 
      if (isspace(term.at(i))) { 
if (0) { 
       wcout << "input term=" << term << endl; 
} 
       // --- simply tokenze term by space and store tokens into 
       // --- the tokens set 
       // --- TODO: check if this really is UTF-8 aware, esp. for 
       // --- strings containing umlauts, etc !! 
       wistringstream iss(term); 
       copy(istream_iterator<wstring, 
         wchar_t, std::char_traits<wchar_t> >(iss), 
        istream_iterator<wstring, 
         wchar_t, std::char_traits<wchar_t> >(), 
        inserter(tokens, tokens.begin())); 
if (0) { 
       wcout << "size of token set=" << tokens.size() << endl; 
       for_each(tokens.begin(), tokens.end(), printSingleToken()); 
} 
      } 
     } 
    } 

    /** 
    * return set of extracted tokens 
    */ 
    set<wstring> getTokens() const { 
     return tokens; 
    } 
}; 

/** 
* Functor to find terms in tokens set 
*/ 
class removeTermIfInPhraseTokensSet { 
private: 
    set<wstring> tokens; 

public: 
    removeTermIfInPhraseTokensSet(const set<wstring>& termTokens) 
    : tokens(termTokens) { 
    } 

    /** 
    * Implements operator() 
    */ 
    bool operator()(const TermNode * tn) const { 
     if (tokens.find(tn->getTerm()) != tokens.end()) { 
      return true; 
     } 
     return false; 
    } 
}; 

... 

findPhrasesAndSplitIntoTokens objPhraseTokens(6.5); 
objPhraseTokens = std::for_each(
    termList.begin(), termList.end(), objPhraseTokens); 
set<wstring> tokens = objPhraseTokens.getTokens(); 
wcout << "size of tokens set=" << tokens.size() << endl; 
for_each(tokens.begin(), tokens.end(), printSingleToken()); 

// --- remove all extracted single tokens from the final terms list 
// --- of similar search terms 
removeTermIfInPhraseTokensSet removeTermIfFound(tokens); 
termList.erase(
    remove_if(
     termList.begin(), termList.end(), removeTermIfFound), 
    termList.end() 
); 

for (vector<TermNode *>::const_iterator tl_iter = termList.begin(); 
     tl_iter != termList.end(); tl_iter++) { 
    wcout << "DEBUG: '" << (*tl_iter)->getTerm() << "' term weight=" << (*tl_iter)->getNormalizedWeight() << endl; 
    if ((*tl_iter)->getNormalizedWeight() <= 6.5) break; 
} 

... 

내가 때문에 내 우분투에서 C++ 11 람다 구문을 사용 could'nt 다음과 같이

업데이트

나는 방식 Scrubbins 권장 중복 단일 용어의 제거를 구현 서버에는 현재 g ++ 4.4.1이 설치되어 있습니다. 어쨌든. 그것은 지금 당장 일을합니다. 갈 길이는 가중치가있는 용어의 품질을 다른 검색 결과 세트로 확인하고 품질을 향상시킬 수있는 방법과 원래의 검색어와 함께 관련성이 높은 용어를 늘릴 수있는 방법을 찾는 것입니다. 할 수있는 일이 쉽지 않을 수도 있습니다. "단순한 경험적 방법"이 있기를 바랍니다. 그러나 조금 더 자세히 살펴보면 또 다른 새로운 질문이 될 수 있습니다. :-)

이렇게 많은 의견을 보내 주셔서 감사합니다.

+2

제목과 같이 보입니다. 약간의 오해의 소지가 있습니다. 이것은 벡터 문제가 아니며, 이것은 텍스트 처리 문제입니다. 하위 문자열을 식별하면 벡터에서 벡터를 제거하는 것은 특히 400 개의 항목 만있는 경우에 특히 간단합니다. – Rook

+1

TermNode 객체에 대한 스마트 포인터의'vector'를 사용하지 않는 이유는 무엇입니까? 그렇게하면,'find' 연산 후에 반환 된 포인터를'삭제 '하는 것을 줄일 수 있습니다. – dirkgently

+0

저는 용어 길이의 역순으로 벡터를 정렬하려고합니다. 벡터를 반복하고 각 단어를 개별 단어로 분리하여 단어를'std :: set '에 놓습니다. 단어가 이미 세트에 존재하면 삭제할 필요가 있다고 플래그를 지정한 다음 벡터 제거에 대해 걱정하십시오. – Rook

답변

5

을 당신이해야 할 일은 첫 번째 목록을 반복하고 분할 : 쓰기 다음

struct YourConditionFunctor { 
    bool operator()(TermNode* term) { 
     if (/* you have to remove term */) { 
      delete term; 
      return true; 
     } 
     return false; 
    } 
}; 

과 모든 다중 단어 값을 단일 단어로 변환합니다. 유니 코드를 허용한다면 ICU의 BreakIterators와 비슷한 것이 필요하다는 것을 의미합니다. 그렇지 않으면 간단한 구두점/공백으로 갈 수 있습니다. 각 문자열이 구성 단어로 분리되면 해시 맵을 사용하여 현재 단어 목록을 모두 유지합니다. 다중 단어 값에 도달하면 단어가 이미 발견되었는지 확인할 수 있습니다. 중복을 식별하는 가장 간단한 방법이어야합니다.

+0

이것은 흥미로운 시작이지만, 부분 집합의 측면을 고려할 때 복잡해집니다. 특히, "Hello World I Love You"에 찬성하여 "Hello World"를 삭제해야한다고 생각합니다. –

+0

@Matthieu : OP는 그런 문제에서 명확한 명세를주지 않았으므로 나는 정말로 논평 할 수 없다. 또한, deque가 기본적으로 사용되어야한다고 말하고 싶습니다. – Scrubbins

+0

질문자가 "하위 문자열"이라고했지만 전체 단어를 보는 것이 더 적절할 수 있음을 알기 위해 +1했습니다. 테스트 케이스는 "어플리케이션"이 "어플리케이션"이있는 상태에서 제거되어야하는지 여부입니다. 또한 토큰 화하기 어려운 유니 코드에 대한 요점은 일부 용도에서는 필수적입니다. –

0

나는 이런 식으로 "erase-remove" 관용구 사용하도록 제안 할 수 있습니다 :

termlist.erase(
    remove_if(
     termlist.begin(), 
     termlist.end(), 
     YourConditionFunctor() 
    ), 
    termlist.end() 
); 
+1

불행히도 이것은 두려운 부분입니다. 제목의 의미와는 달리 문제의 핵심은 제거 할 용어를 식별하는 것입니다. 게다가, 나는 술어에서 항목을 실제로'삭제 '하는 것은 아주 나쁜 습관이라고 생각한다. 이것은 일반적으로 호출되는 객체를 수정하지 않는 조건부의 모든 규칙적인 사용에 반대하며 재난을위한 처방처럼 들립니다. –

+0

예, Matthieu와 동의해야합니다.이를 수행하는 방법은 내 질문 텍스트에 명시된 질문에 표시됩니다. 나는 stackoverflow에서 여기에 대답의 무리가 * 어떻게 * 이것을 사용하는 보여줍니다 생각합니다. 내 문제는 용어를 정리하는 법과 숙어 위에 사용할 수있는 올바른 컨테이너를 선택하는 방법이다. –

관련 문제