रिवर्स इटरेटर के साथ इरेज़ को कैसे कॉल करें


181

मैं ऐसा कुछ करने की कोशिश कर रहा हूं:

for ( std::list< Cursor::Enum >::reverse_iterator i = m_CursorStack.rbegin(); i != m_CursorStack.rend(); ++i )
{
    if ( *i == pCursor )
    {
        m_CursorStack.erase( i );
        break;
    }
}

हालांकि मिटा एक पुनरावृत्ति लेता है और रिवर्स पुनरावृत्ति नहीं। क्या एक रिवर्स पुनरावृत्ति को एक नियमित पुनरावृत्ति में बदलने का एक तरीका है या इस तत्व को सूची से हटाने का कोई अन्य तरीका है?


17
एक तरफ के रूप में, जब इन जैसे लूप लिखते हैं, तो बार-बार अंतिम पुनरावृत्ति की गणना न करें जैसा कि आप यहां करते हैं i != m_CursorStack.rend()। इसके बजाय, लिखें i = m_CursorStack.rbegin(), end = m_CursorStack.rend(); i != end;। यही है, एक पुनरावृत्त को इनिशियलाइज़ करें जिसे आप बार-बार तुलना के लिए रख सकते हैं - यह मानते हुए कि अंतिम स्थिति आपके लूप बॉडी के साइड इफेक्ट के रूप में नहीं बदल रही है।
सेह

यह मुझे लगता है कि यहाँ स्पष्ट प्रश्न यह होगा कि आप ऐसा क्यों कर रहे हैं। आप सूची को उल्टा करने से क्या हासिल करते हैं? उपयोग करने के बजाय इस कोड को स्वयं लिखने से आपको क्या लाभ होगा std::remove?
जेरी कॉफ़िन

और एक std पर एक पुनरावृत्ति :: सूची अभी भी वृद्धि के लिए मान्य है जिसके बाद यह संदर्भित तत्व मिटा दिया गया है?
स्टीव जेसप

3
मैं केवल 1 तत्व निकालना चाहता हूं, इस प्रकार 'विराम', 'हटाने' का उपयोग करने से उस मैच से अधिक समय तक छुटकारा मिलेगा और जो मैं चाहता हूं वह नहीं करूंगा। जिस तत्व को मैं इस विशेष मामले में निकालना चाहता हूं, वह लगभग हमेशा सूची का अंत होगा या इसके बहुत करीब होगा, इसलिए रिवर्स में पुनरावृति भी जल्दी और समस्या के लिए बेहतर अनुकूल है।
0xC0DEFACE 3

4
stackoverflow.com/a/2160581/12386 यह कहते हैं कि डिज़ाइनर विशेष रूप से कार्यान्वयन को परिभाषित नहीं करते हैं क्योंकि आप उपयोगकर्ता को जानने या देखभाल करने वाले नहीं हैं, फिर भी @seh हमें जादुई रूप से सिर्फ यह पता चलता है कि रेंडर () की गणना की गई है। और महंगा है।
स्टु

जवाबों:


181

कुछ और शोध और परीक्षण के बाद मुझे इसका हल मिला। मानक के अनुसार स्पष्ट रूप से [24.4.1 / 1] i.base () और i के बीच का संबंध है:

&*(reverse_iterator(i)) == &*(i - 1)

( डॉ। डॉब्स लेख से ):

वैकल्पिक शब्द

इसलिए आपको आधार प्राप्त करते समय एक ऑफसेट लागू करना होगा ()। इसलिए समाधान है:

m_CursorStack.erase( --(i.base()) );

संपादित करें

C ++ 11 के लिए अद्यतन करना।

रिवर्स_िटरेटर iअपरिवर्तित है:

m_CursorStack.erase( std::next(i).base() );

Rev_iterator iउन्नत है:

std::advance(i, 1);
m_CursorStack.erase( i.base() );

मुझे अपने पिछले समाधान की तुलना में यह बहुत स्पष्ट लगता है। आपको जिसकी भी आवश्यकता हो, उसका उपयोग करें।


27
आपको अपने द्वारा उद्धृत लेख का थोड़ा और अधिक ध्यान रखना चाहिए - पोर्टेबल होने के लिए अभिव्यक्ति होना चाहिए m_CursorStack.erase( (++i).base())(आदमी, रिवर्स पुनरावृत्तियों के साथ यह सामान करना मेरे सिर को चोट पहुंचाता है ...)। यह भी ध्यान दिया जाना चाहिए कि डीडीजे लेख मेयर की "प्रभावी एसटीएल" पुस्तक में शामिल है।
माइकल बुर

8
मुझे लगता है कि आरेख सहायक से अधिक भ्रामक है। चूंकि rbegin, ri और रेंडरिंग सभी वास्तव में उस तत्व के दाईं ओर इंगित करते हैं जो उन्हें इंगित करने के लिए खींचा जाता है। आरेख से पता चलता है कि यदि आप *उन्हें ले जाते हैं तो आप किस तत्व तक पहुंच सकते हैं, लेकिन हम बात कर रहे हैं कि यदि आप baseउन्हें किस तत्व की ओर संकेत कर रहे हैं, जो कि एक तत्व है। मैं इस तरह के --(i.base())या (++i).base()समाधान के प्रशंसक नहीं हूँ क्योंकि वे पुनरावृत्ति करने वाले को उत्परिवर्तित करते हैं। मैं पसंद (i+1).base()करता हूं जो काम करता है।
मागुका

4
उलटा चलने वाले झूठे होते हैं .. जब आस्थगित किया जाता है, तो एक उल्टा चलने वाला तत्व इसके पहले तत्व को वापस कर देता हैयहाँ देखें
bobobobo

4
बस बिल्कुल स्पष्ट होने के लिए, इस तकनीक को अभी भी लूप के लिए सामान्य रूप से उपयोग नहीं किया जा सकता है (जहां इट्रेटर सामान्य तरीके से बढ़ाया जाता है)। देखें stackoverflow.com/questions/37005449/...
logidelic

1
m_CursorStack.erase ((++ i) .base ()) ऐसा लगता है कि यह एक समस्या होगी अगर ++ मैं आपको अंतिम तत्व पिछले करने के लिए मिला। क्या आप अंत में मिटा सकते हैं ()?
स्टु

16

कृपया ध्यान दें कि m_CursorStack.erase( (++i).base())यदि forलूप में उपयोग किया जाता है तो यह एक समस्या हो सकती है (मूल प्रश्न देखें) क्योंकि यह i का मान बदलता है। सही अभिव्यक्ति हैm_CursorStack.erase((i+1).base())


4
आपको iterator j = i ; ++ji+1
पुनरावृत्त

3
@ बोबोबोबो, आप m_CursorStack.erase(boost::next(i).base())बूस्ट के साथ उपयोग कर सकते हैं । या C ++ 11 मेंm_CursorStack.erase(std::next(i).base())
alfC

12

... या इस तत्व को सूची से निकालने का कोई और तरीका?

इसके लिए -std=c++11ध्वज की आवश्यकता होती है auto:

auto it=vt.end();
while (it>vt.begin())
{
    it--;
    if (*it == pCursor) //{ delete *it;
        it = vt.erase(it); //}
}

एक आकर्षण काम करता है :)
डेन-जेसन

@ गीतानोमेन्डोला: क्यों?
स्लैशमाईज़

3
आपको कौन गारंटी देता है कि सूची में पुनरावृत्तियों का आदेश दिया गया है?
गायनो मेंडोला

7

मजेदार है कि इस पृष्ठ पर अभी तक कोई सही समाधान नहीं है। तो, निम्नलिखित सही है:

अग्रसारणक के मामले में समाधान सीधे आगे है:

std::list< int >::iterator i = myList.begin();
while ( ; i != myList.end(); ) {
  if ( *i == to_delete ) {
    i = myList.erase( i );
  } else {
    ++i;
  } 
}

रिवर्स इटरेटर के मामले में आपको वही करने की आवश्यकता है:

std::list< int >::reverse_iterator i = myList.rbegin();
while ( ; i != myList.rend(); ) {
  if ( *i == to_delete ) {
    i = decltype(i)(myList.erase( std::next(i).base() ));
  } else {
    ++i;
  } 
}

टिप्पणियाँ:

  • आप reverse_iteratorएक पुनरावृत्त से निर्माण कर सकते हैं
  • आप के रिटर्न मान का उपयोग कर सकते हैं std::list::erase

यह कोड काम करता है, लेकिन कृपया बताएं कि अगला क्यों उपयोग किया जाता है और दुनिया को ढहने के बिना एक रिवर्स पुनरावृत्ति के लिए आगे चलने वाले को कैसे डालना सुरक्षित है
लेटरिस ई

1
@ इसके बाद यह एक कास्ट नहीं है। यह एक इंटरएटर से बाहर एक नया रिवर्स इटेटर बनाता है। यह रिवर्स इटरेटर का सामान्य कंस्ट्रक्टर है।
.मोन तोथ

3

यहाँ reverse_iterator's' base()पद्धति का उपयोग करते हुए और परिणाम को घटाते हुए, यह ध्यान देने योग्य है कि reverse_iterators को नियमित iterators के समान दर्जा नहीं दिया गया है । सामान्य तौर पर, आपको इस तरह के सटीक कारणों के लिए नियमित iteratorएस से reverse_iteratorएस (साथ ही साथ const_iteratorएस और const_reverse_iteratorएस) को प्राथमिकता देना चाहिए । क्यों की गहन चर्चा के लिए डॉक्टर डोब्स जर्नल देखें ।


3
typedef std::map<size_t, some_class*> TMap;
TMap Map;
.......

for( TMap::const_reverse_iterator It = Map.rbegin(), end = Map.rend(); It != end; It++ )
{
    TMap::const_iterator Obsolete = It.base();   // conversion into const_iterator
    It++;
    Map.erase( Obsolete );
    It--;
}

3

और यहाँ एक कंटेनर में एक तत्व को मिटाने के लिए एक रिवर्स इट्रेटर में वापस मिटाए जाने के परिणाम को परिवर्तित करने के लिए कोड का टुकड़ा है, जो रिवर्स में पुनरावृति करता है। थोड़ा अजीब है, लेकिन यह तब भी काम करता है जब पहले या आखिरी तत्व को मिटा दिया जाता है:

std::set<int> set{1,2,3,4,5};

for (auto itr = set.rbegin(); itr != set.rend(); )
{    
    if (*itr == 3)
    {
        auto it = set.erase(--itr.base());
        itr = std::reverse_iterator(it);            
    }
    else
        ++itr;
}

2

यदि आपको सब कुछ मिटाने की जरूरत नहीं है, तो आप समस्या को हल करने के लिए, आप समस्या को दूर करने के लिए मुहावरे का उपयोग कर सकते हैं:

m_CursorStack.erase(std::remove(m_CursorStack.begin(), m_CursorStack.end(), pCursor), m_CursorStack.end());

std::removeकंटेनर में सभी आइटम स्वैप करता pCursorहै जो अंत तक मेल खाता है, और पहले मैच आइटम के लिए एक पुनरावृत्ति देता है। फिर, eraseएक श्रेणी का उपयोग करते हुए पहले मैच से मिट जाएगा, और अंत में जाएं। गैर-मिलान तत्वों का क्रम संरक्षित है।

यदि आप एक का उपयोग कर रहे हैं std::vector, तो यह आपके लिए तेजी से काम कर सकता है , जहां सामग्री के बीच में मिटा देने से बहुत अधिक नकल या चलती हो सकती है।

या निश्चित रूप से, ऊपर दिए गए उत्तर reverse_iterator::base()दिलचस्प और उपयोग के बारे में बता रहे हैं, बताए गए सटीक समस्या को हल करने के लिए, मैं std::removeबेहतर तर्क देता हूं ।


1

बस कुछ स्पष्ट करना चाहता था: उपरोक्त कुछ टिप्पणियों और उत्तरों में इरेज़ के लिए पोर्टेबल संस्करण का उल्लेख किया गया है (++ i) .base ()। हालाँकि, जब तक मुझे कुछ याद नहीं आ रहा है, तब तक सही कथन है (++ ri) .base (), जिसका अर्थ है कि आप 'increment' को '' '' '' '' '' 'सेपरेटर' 'नहीं' '' itter '' 'बनाते हैं।

मैं कल कुछ ऐसा ही करने की जरूरत में दौड़ा और यह पोस्ट मददगार थी। सबको धन्यवाद।


0

अन्य उत्तरों के पूरक के लिए और क्योंकि मैं इस सवाल पर ठोकर खाए हुए हूं, जबकि std :: string के बारे में बहुत सफलता के बिना खोज की जा रही है, यहाँ std :: string, std :: string :: erase और std :: रिवर्स_ राइटर के उपयोग के साथ प्रतिक्रिया मिलती है।

मेरी समस्या एक पूर्ण फ़ाइल नाम स्ट्रिंग से छवि फ़ाइल नाम मिटा रही थी। यह मूल रूप से std :: string :: find_last_of के साथ हल किया गया था, फिर भी मैं std के साथ एक वैकल्पिक तरीका खोजता हूं: रिवर्स_ राइटर।

std::string haystack("\\\\UNC\\complete\\file\\path.exe");
auto&& it = std::find_if( std::rbegin(haystack), std::rend(haystack), []( char ch){ return ch == '\\'; } );
auto&& it2 = std::string::iterator( std::begin( haystack ) + std::distance(it, std::rend(haystack)) );
haystack.erase(it2, std::end(haystack));
std::cout << haystack;  ////// prints: '\\UNC\complete\file\'

यह एल्गोरिथ्म, पुनरावृत्ति और स्ट्रिंग हेडर का उपयोग करता है।


0

रिवर्स इटरेटर का उपयोग करना काफी कठिन है। तो बस सामान्य पुनरावृत्ति इस्तेमाल किया। 'r' यह अंतिम तत्व से शुरू होता है। जब कुछ मिटाने को मिल जाए। इसे मिटाएं और अगले पुनरावृत्त पर लौटें। उदाहरण के लिए जब 3 तत्व को हटाएं तो यह वर्तमान 4 तत्व को इंगित करेगा। और नया तीसरा। तो यह बाईं ओर ले जाने के लिए 1 घटाया जाना चाहिए

void remchar(string& s,char c)
{      
    auto r = s.end() - 1;
    while (r >= s.begin() && *r == c)
    {
        r = s.erase(r);
        r -= 1;
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.