2011-03-22 25 views
21

the other topic में मैं this समस्या हल करने का प्रयास कर रहा था। समस्या std::string से डुप्लिकेट वर्णों को निकालना था।`std :: set` के साथ क्या गलत है?

std::string s= "saaangeetha"; 

के बाद से क्रम महत्वपूर्ण नहीं था, इसलिए मैं s पहले हल, और फिर std::unique इस्तेमाल किया और अंत में the desired result पाने के लिए इसे पुनः आकार:

aeghnst 

यह सही है!


अब मैं वही करना चाहता हूं, लेकिन साथ ही मैं अक्षरों का क्रम बरकरार रखना चाहता हूं।

sangeth 

तो मैं this लिखा है::

template<typename T> 
struct is_repeated 
{ 
    std::set<T> unique; 
    bool operator()(T c) { return !unique.insert(c).second; } 
}; 
int main() { 
    std::string s= "saaangeetha"; 
    s.erase(std::remove_if(s.begin(), s.end(), is_repeated<char>()), s.end()); 
    std::cout << s ; 
} 

कौन इस उत्पादन देता है: इसका मतलब है, मैं इस उत्पादन चाहते

saangeth 

है, a दोहराया है, हालांकि अन्य repetitions चला गया। कोड के साथ क्या गलत है?

वैसे भी मैं थोड़ा change my code: (टिप्पणी को देख)

template<typename T> 
struct is_repeated 
{ 
    std::set<T> & unique; //made reference! 
    is_repeated(std::set<T> &s) : unique(s) {} //added line! 
    bool operator()(T c) { return !unique.insert(c).second; } 
}; 
int main() { 
    std::string s= "saaangeetha"; 
    std::set<char> set; //added line! 
    s.erase(std::remove_if(s.begin(),s.end(),is_repeated<char>(set)),s.end()); 
    std::cout << s ; 
} 

आउटपुट:

sangeth 

समस्या चला गया!

तो पहले समाधान के साथ क्या गलत है?

इसके अलावा, अगर मैं सदस्य चर unique संदर्भ प्रकार नहीं बनाता, तो the problem doesn't go

std::set या is_repeated फ़ैक्टर के साथ क्या गलत है? वास्तव में समस्या कहां है?

मैं यह भी ध्यान देता हूं कि यदि is_repeated फ़ैक्टर की प्रतिलिपि बनाई गई है, तो इसके प्रत्येक सदस्य की भी प्रतिलिपि बनाई जाती है। मुझे यहाँ समस्या नहीं दिख रही है!

+6

, डेटा छँटाई हे है: या तो अपने functor refactor और यह राज्यविहीन बनाने या "कार्य करने के लिए टेम्पलेट्स (एल्गोरिदम) संदर्भ पारित करने के लिए है कि आमतौर पर उनके तर्कों की प्रतियां ले जाएगा" Boost.Ref करने के लिए उपयोग, इसलिए की तरह (एन लॉग एन)। ऐसा करने के लिए एक और अधिक कुशल एल्गोरिदम है। प्रत्येक चरित्र की घटनाओं की गिनती, स्ट्रिंग का रैखिक पास बनाएं। स्ट्रिंग का दूसरा पास बनाएं और गिनती 1 है तो केवल आउटपुट करें। यह ओ (एन) है। –

+7

आपको दो पास की आवश्यकता नहीं है। बस एक बूल सरणी बनाएं और जांचें कि क्या bFound [c] == सत्य है अगर आप इसे परिणाम स्ट्रिंग में जोड़ते हैं और bFound [c] = true सेट करते हैं। इस तरह आप एक फ़िल्टर बनाते हैं और इसे एक पास में उपयोग करते हैं। –

+0

@ जेफ: या किसी सेट के बजाय एक unordered_set का उपयोग करें। – kennytm

उत्तर

15

In GCC (libstdc++), remove_if रूप

template<typename It, typename Pred> 
    It remove_if(It first, It last, Pred predicate) { 
     first = std::find_if(first, last, predicate); 
    //         ^^^^^^^^^ 
     if (first == last) 
     return first; 
     else { 
     It result = first; 
     ++ result; 
     for (; first != last; ++ first) { 
      if (!predicate(*first)) { 
    //   ^^^^^^^^^ 
       *result = std::move(*first); 
       ++ result; 
      } 
     } 
     } 
    } 

ध्यान रखें कि आपके विधेय find_if को दर-मूल्य पारित हो जाता है, तो struct, और इसलिए सेट अनिवार्य रूप से लागू किया गया है, find_if अंदर संशोधित करने के लिए वापस प्रचारित नहीं किया जाएगा फोन करने वाले।

पहले डुप्लिकेट दिखाई देता है के बाद से पर:

saaangeetha 
//^

प्रारंभिक "sa"find_if कॉल के बाद रखा जाएगा। इस बीच, predicate का सेट खाली है (find_if के भीतर सम्मिलन स्थानीय हैं)। इसलिए लूप बाद में तीसरे a रखेगा।

sa | angeth 
// ^^ ^^^^^^ 
// || kept by the loop in remove_if 
// || 
// kept by find_if 
+0

+1। यह व्यवहार बताता है। यह सब 'std :: remove_if' के कार्यान्वयन पर निर्भर करता है। – Nawaz

+0

+1: उत्कृष्ट विश्लेषण। –

+0

ठीक है। इन सभी विश्लेषणों की निचली रेखा, और सीखना यह है कि: ** मज़ेदार के पास राज्य नहीं होना चाहिए **। सही? – Nawaz

4

मुझे लगता है कि समस्या यह हो सकती है कि is_repeated फ़ैक्टर को std::remove_if के कार्यान्वयन के अंदर कहीं कॉपी किया गया है। यदि ऐसा है, तो डिफ़ॉल्ट प्रतिलिपि कन्स्ट्रक्टर का उपयोग किया जाता है और यह बदले में std::set कॉपी कन्स्ट्रक्टर को कॉल करता है। आप दो is_repeated मज़ेदारों के साथ समाप्त होते हैं जो संभवतः स्वतंत्र रूप से उपयोग किए जाते हैं। हालांकि, उनमें से दोनों सेट अलग-अलग वस्तुएं हैं, इसलिए आप पारस्परिक परिवर्तन नहीं देखते हैं। यदि आप किसी संदर्भ में is_repeated::unique फ़ील्ड को चालू करते हैं, तो कॉपी किए गए फ़ैक्टर अभी भी मूल सेट का उपयोग करते हैं जो आप इस मामले में चाहते हैं।

+0

मैंने अपना प्रश्न संपादित किया। कृपया पिछली पंक्ति पढ़ें जहां मैंने कहा था: यदि 'is_repeated' फ़ैक्टर की प्रतिलिपि बनाई गई है, तो इसके प्रत्येक सदस्य की भी प्रतिलिपि बनाई जाती है। मुझे यहाँ समस्या नहीं दिख रही है! – Nawaz

+0

@ नवाज़: लेकिन यह * कॉपी किया गया है http://ideone.com/Hm8zG – kennytm

+0

जैसा कि मैंने लिखा है, यदि सदस्यों की प्रतिलिपि बनाई गई है तो आपके पास दो 'अद्वितीय' सेट हैं। उनमें से दोनों शुरुआत में खाली हो सकते थे। तब दोनों का उपयोग यह जांचने के लिए किया जा सकता है कि चरित्र 'ए' मौजूद है या नहीं। – julkiewicz

17

फ़ंक्शंस को ऐसे तरीके से डिज़ाइन किया जाना चाहिए जहां एक मज़ेदार की एक प्रति मूल मज़ेदार के समान हो। यही है, यदि आप एक मज़ेदार की एक प्रति बनाते हैं और फिर संचालन का अनुक्रम करते हैं, तो परिणाम वही होना चाहिए चाहे आप किस मज़ेदार का उपयोग करें, या फिर भी यदि आप दो मज़दूरों को अंतःस्थापित करते हैं। यह एसटीएल कार्यान्वयन को मल्टीक्टरों की प्रतिलिपि बनाने के लिए लचीलापन देता है और फिट बैठता है।

अपने पहले मज़ेदार के साथ, यह दावा नहीं है क्योंकि यदि मैं आपके मज़ेदार की प्रतिलिपि बनाता हूं और फिर इसे कॉल करता हूं, तो आप अपने संग्रहीत सेट में किए गए परिवर्तन मूल मज़ेदार में प्रतिबिंबित नहीं होते हैं, इसलिए प्रतिलिपि और मूल अलग-अलग प्रदर्शन करेंगे । इसी तरह, यदि आप अपना दूसरा मज़ेदार लेते हैं और इसे संदर्भ द्वारा अपने सेट को स्टोर नहीं करते हैं, तो मज़ेदार की दो प्रतियां समान रूप से व्यवहार नहीं करतीं।

कारण यह है कि फंक्टर का आपका अंतिम संस्करण काम करता है, क्योंकि तथ्य यह है कि सेट संदर्भ द्वारा संग्रहीत किया जाता है, इसका मतलब है कि मंगल ग्रहक की किसी भी प्रतियां एक दूसरे के समान व्यवहार करती हैं।

आशा है कि इससे मदद मिलती है!

+0

आपका मतलब है 'std :: remove_if' मज़ेदार की प्रति बनाता है? क्या आप मुझे कोड दिखा सकते हैं? 'Std :: remove_if' का यह कार्यान्वयन प्रतिलिपि नहीं बनाता है: http://www.cplusplus.com/reference/algorithm/remove_if/ – Nawaz

+4

यह वास्तव में एसटीएल के आपके कार्यान्वयन पर निर्भर करता है; मुझे नहीं लगता कि मानक कहता है कि यह एक प्रतिलिपि बनाना चाहिए या नहीं। हालांकि, मुझे पूरा यकीन है कि मानक कहता है कि यह ** एक प्रतिलिपि बना सकता है, और आउटपुट के आधार पर यह बहुत ऐसा लगता है जैसे यह ऐसा कर रहा है। – templatetypedef

+0

उत्तर सही है। आम तौर पर आपके मज़ेदारों के पास राज्य नहीं होना चाहिए क्योंकि वे प्रतिलिपि प्राप्त कर सकते हैं। –

5

नहीं वास्तव में एक जवाब है, लेकिन के रूप में एक और दिलचस्प tidbit विचार करने के लिए, इस भले ही यह मूल functor का उपयोग करता है काम करता है,:

#include <set> 
#include <iostream> 
#include <string> 
#include <algorithm> 
#include <iterator> 

template<typename T> 
struct is_repeated { 
    std::set<T> unique; 
    bool operator()(T c) { return !unique.insert(c).second; } 
}; 
int main() { 
    std::string s= "saaangeetha"; 
    std::remove_copy_if(s.begin(), s.end(), 
         std::ostream_iterator<char>(std::cout), 
         is_repeated<char>()); 
    return 0; 
} 

संपादित करें: मैं इसे इस व्यवहार को प्रभावित करता नहीं लगता है, लेकिन मैं मैंने आपके मज़ेदार (ऑपरेटर() में एक मामूली पर्ची को भी सही किया है, जाहिर है कि टाइप टी का पैरामीटर लेना चाहिए, char नहीं)।

+0

+1000। दिलचस्प मोड़। Hahaha। http://ideone.com/FfvbJ – Nawaz

+0

और मामूली सुधार के लिए धन्यवाद! – Nawaz

2

फ़ैक्टर कक्षाएं शुद्ध कार्य होनी चाहिए और उनके पास कोई राज्य नहीं होना चाहिए। इस पर एक अच्छी व्याख्या के लिए स्कॉट मेयर के प्रभावी एसटीएल पुस्तक में आइटम 39 देखें। लेकिन इसका अर्थ यह है कि आपके मज़ेदार वर्ग को एल्गोरिदम के अंदर 1 या अधिक बार कॉपी किया जा सकता है।

1

अन्य उत्तरों सही हैं, इस समस्या में यह है कि आप जिस मिक्सर का उपयोग कर रहे हैं वह कॉपी करने योग्य सुरक्षित नहीं है। विशेष रूप से, एसटीएल जो जीसीसी (4.2) के साथ आता है std::find_if के संयोजन के रूप में लागू करने के लिए पहले तत्व का पता लगाने के लिए std::remove_copy_if के बाद ऑपरेशन को पूरा करने के लिए लागू करता है।

template <typename ForwardIterator, typename Predicate> 
std::remove_if(ForwardIterator first, ForwardIterator end, Predicate pred) { 
    first = std::find_if(first, end, pred); // [1] 
    ForwardIterator i = it; 
    return first == last? first 
      : std::remove_copy_if(++i, end, fist, pred); // [2] 
} 

में [1] का मतलब है कि पहला तत्व पाया functor की प्रति में जोड़ा जाता है और इसका मतलब है कि पहले 'एक' गुमनामी में खो जाएगा प्रतिलिपि। मज़ेदार की भी प्रतिलिपि बनाई जाती है [2], और यह ठीक होगा अगर ऐसा नहीं था क्योंकि उस प्रतिलिपि के लिए मूल एक खाली मजेदार है।

1

remove_if के कार्यान्वयन के आधार पर आपके भविष्य की प्रतियां बना सकते हैं।एक अलग रूप में के रूप में

#include <set> 
#include <iostream> 
#include <string> 
#include <algorithm> 
#include <iterator> 

#include <boost/ref.hpp> 
#include <boost/bind.hpp> 

template<typename T> 
struct is_repeated { 
    std::set<T> unique; 
    bool operator()(T c) { return !unique.insert(c).second; } 
}; 

int main() { 
    std::string s= "saaangeetha"; 
    s.erase(std::remove_if(s.begin(), s.end(), boost::bind<bool>(boost::ref(is_repeated<char>()),_1)), s.end()); 
    std::cout << s; 

    return 0; 
} 
संबंधित मुद्दे