इसे पुनरावृति करते हुए मानचित्र से कैसे निकालें?


177

मैं इसे पुन: प्रदर्शित करते हुए मानचित्र से कैसे निकालूं? पसंद:

std::map<K, V> map;
for(auto i : map)
    if(needs_removing(i))
        // remove it from the map

यदि मैं इसका उपयोग map.eraseकरता हूं तो पुनरावृत्तियों को अमान्य कर दूंगा



इससे भी अधिक समान: stackoverflow.com/questions/800955/…
क्लेस्ट


इसी विषय पर: stackoverflow.com/questions/263945/…
Agostino

जवाबों:


280

मानक सहयोगी-कंटेनर मिटाना मुहावरे:

for (auto it = m.cbegin(); it != m.cend() /* not hoisted */; /* no increment */)
{
  if (must_delete)
  {
    m.erase(it++);    // or "it = m.erase(it)" since C++11
  }
  else
  {
    ++it;
  }
}

ध्यान दें कि हम वास्तव में forयहां एक साधारण लूप चाहते हैं, क्योंकि हम कंटेनर को ही संशोधित कर रहे हैं। रेंज-आधारित लूप उन स्थितियों के लिए कड़ाई से आरक्षित होना चाहिए जहां हम केवल तत्वों के बारे में परवाह करते हैं। RBFL के लिए सिंटैक्स लूप बॉडी के अंदर कंटेनर को उजागर नहीं करने से यह स्पष्ट करता है।

संपादित करें। Pre-C ++ 11, आप const-iterators को मिटा नहीं सकते। वहाँ आपको कहना होगा:

for (std::map<K,V>::iterator it = m.begin(); it != m.end(); ) { /* ... */ }

एक कंटेनर से एक तत्व को मिटाना तत्व की कमी के साथ बाधाओं पर नहीं है। सादृश्य द्वारा, यह हमेशा पूरी तरह से वैध रहा है delete pकि pपॉइंटर-टू-कॉन्स्टेंट कहां है। निरंतरता जीवन भर विवश नहीं करती है; C ++ में const मान अभी भी मौजूदा रोक सकते हैं।


1
"लूप बॉडी के अंदर कंटेनर को उजागर भी नहीं किया जा रहा है" आपका क्या मतलब है?
दानी

2
@ डैनी: ठीक है, 20 वीं सदी के निर्माण के विपरीत for (int i = 0; i < v.size(); i++)। यहाँ हमें v[i]पाश के अंदर कहना है, अर्थात हमें कंटेनर का स्पष्ट रूप से उल्लेख करना चाहिए। दूसरी ओर RBFL लूप वैरिएबल का परिचय देता है जो मूल्य के रूप में सीधे प्रयोग करने योग्य है, और इसलिए लूप के अंदर कंटेनर का कोई ज्ञान आवश्यक नहीं है। यह उन छोरों के लिए आरबीएफएल के इच्छित उपयोग का एक सुराग है जिन्हें कंटेनर के बारे में जानना नहीं है। मिटा देना पूर्ण विपरीत स्थिति है, जहां यह सभी कंटेनर के बारे में है।
केरेक एसबी

3
@ कर्कशः वास्तव में। यह पोस्ट-इन्क्रीमेंट के वैध उपयोगों में से एक है: अगला, मान्य इटरेटर पाने के लिए पहला इन्क्रीमेंट it, और फिर पुराने को मिटा दें। यह दूसरे तरीके से काम नहीं करता है!
केरेक एसबी

5
मैंने कहीं पढ़ा है कि C ++ 11 में, it = v.erase(it); अब नक्शों के लिए भी काम करता है। सभी साहचर्य तत्वों पर erase () अब अगला संचालक लौटाता है। इसलिए पुराने कीचड़ को हटाने () के भीतर पोस्ट-इंक्रीमेंट ++ की आवश्यकता होती है, जिसकी अब आवश्यकता नहीं है। यह (अगर सच है) एक अच्छी बात है, जैसा कि कीचड़ ओवरराइड-पोस्ट-इन्क्रीमेंट-इन-ए-फंक्शन-कॉल मैजिक पर निर्भर करता है, "फिक्स्ड" नौसिखिया रक्षकों द्वारा फंक्शन कॉल से वेतन वृद्धि लेने के लिए, या इसे स्वैप करने के लिए। एक पूर्वाभास के लिए "क्योंकि यह सिर्फ एक स्टाइल की बात है", आदि
डेवी मॉर्गन

3
आप कॉल और ब्लॉक it++में क्यों आएंगे ? यह एक बार इन के बाद फोन करने के लिए पर्याप्त नहीं होगा ? if else
nburk

25

मैं व्यक्तिगत रूप से इस पैटर्न को पसंद करता हूं जो एक अतिरिक्त चर की कीमत पर थोड़ा स्पष्ट और सरल है:

for (auto it = m.cbegin(), next_it = it; it != m.cend(); it = next_it)
{
  ++next_it;
  if (must_delete)
  {
    m.erase(it);
  }
}

इस दृष्टिकोण के लाभ:

  • लूप के लिए वृद्धिशील एक वृद्धिशील के रूप में समझ में आता है;
  • मिटा आपरेशन वृद्धि तर्क के साथ मिश्रित होने के बजाय एक सरल मिटा है;
  • लूप बॉडी की पहली पंक्ति के बाद, का अर्थ itऔरnext_it पूरे पुनरावृत्ति में बने रहना, आपको आसानी से अतिरिक्त स्टेटमेंट जोड़ने की अनुमति देता है बिना हेडस्क्रेच किए बिना कि वे उद्देश्य के अनुसार काम करेंगे (सिवाय इसके कि आप itइसे मिटाने के बाद उपयोग नहीं कर सकते हैं ।

2
मैं वास्तव में एक और लाभ के बारे में सोच सकता हूं, अगर लूप कोड में कॉल करता है जो उस प्रविष्टि को मिटा देता है जो पहले या पिछले वाले पर पुनरावृत्त होता है (और लूप इससे अनजान है) यह बिना किसी नुकसान के काम करेगा। एकमात्र प्रतिबंध यह है कि क्या कुछ मिटा रहा है जिसे अगले द्वारा या उत्तराधिकारियों द्वारा इंगित किया जा रहा है। एक पूरी तरह से साफ़ की गई सूची / नक्शे को भी खिलाफ परीक्षण किया जा सकता है।
लार्सवाद

यह उत्तर सरल और स्पष्ट है, भले ही लूप अधिक जटिल हो और जिसमें विभिन्न कार्यों को हटाने या न करने का निर्णय करने के लिए तर्क के कई स्तर हों। हालांकि, मैंने इसे थोड़ा सरल बनाने के लिए एक संपादन प्रस्तावित किया है। "next_it" टाइपो से बचने के लिए init के लिए "it" पर सेट किया जा सकता है, और चूंकि init और iteration स्टेटमेंट दोनों इसे सेट करते हैं और next_it समान मानों के लिए, आपको "next_it =" कहने की आवश्यकता नहीं है; लूप की शुरुआत में।
cdgraham

1
इस उत्तर का उपयोग करने वाले किसी भी व्यक्ति को ध्यान में रखें: आपके पास लूप के लिए "++ next_it" होना चाहिए, और पुनरावृति अभिव्यक्ति में नहीं। यदि आप इसे "it = next_it ++" के रूप में पुनरावृति अभिव्यक्ति में स्थानांतरित करने का प्रयास करते हैं, तो अंतिम पुनरावृति पर, जब "यह" "m.cend ()" के बराबर सेट होने जा रहा है, तो आप "next_it" को पुन: व्यवस्थित करने का प्रयास करेंगे। पिछले "m.cend ()", जो त्रुटिपूर्ण है।
cdgraham

6

संक्षेप में "मैं इसे पुनरावृत्त करते हुए मानचित्र से कैसे निकालूं?"

  • पुराने नक्शे में निहित: आप नहीं कर सकते
  • नए नक्शे के साथ: लगभग @KerrekSB ने सुझाव दिया। लेकिन उन्होंने जो कुछ पोस्ट किया उसमें कुछ वाक्यविन्यास मुद्दे हैं।

GCC के नक्शे में निहित है (ध्यान दें GXX_EXPERIMENTAL_CXX0X ):

#ifdef __GXX_EXPERIMENTAL_CXX0X__
      // _GLIBCXX_RESOLVE_LIB_DEFECTS
      // DR 130. Associative erase should return an iterator.
      /**
       *  @brief Erases an element from a %map.
       *  @param  position  An iterator pointing to the element to be erased.
       *  @return An iterator pointing to the element immediately following
       *          @a position prior to the element being erased. If no such 
       *          element exists, end() is returned.
       *
       *  This function erases an element, pointed to by the given
       *  iterator, from a %map.  Note that this function only erases
       *  the element, and that if the element is itself a pointer,
       *  the pointed-to memory is not touched in any way.  Managing
       *  the pointer is the user's responsibility.
       */
      iterator
      erase(iterator __position)
      { return _M_t.erase(__position); }
#else
      /**
       *  @brief Erases an element from a %map.
       *  @param  position  An iterator pointing to the element to be erased.
       *
       *  This function erases an element, pointed to by the given
       *  iterator, from a %map.  Note that this function only erases
       *  the element, and that if the element is itself a pointer,
       *  the pointed-to memory is not touched in any way.  Managing
       *  the pointer is the user's responsibility.
       */
      void
      erase(iterator __position)
      { _M_t.erase(__position); }
#endif

पुरानी और नई शैली के साथ उदाहरण:

#include <iostream>
#include <map>
#include <vector>
#include <algorithm>

using namespace std;
typedef map<int, int> t_myMap;
typedef vector<t_myMap::key_type>  t_myVec;

int main() {

    cout << "main() ENTRY" << endl;

    t_myMap mi;
    mi.insert(t_myMap::value_type(1,1));
    mi.insert(t_myMap::value_type(2,1));
    mi.insert(t_myMap::value_type(3,1));
    mi.insert(t_myMap::value_type(4,1));
    mi.insert(t_myMap::value_type(5,1));
    mi.insert(t_myMap::value_type(6,1));

    cout << "Init" << endl;
    for(t_myMap::const_iterator i = mi.begin(); i != mi.end(); i++)
        cout << '\t' << i->first << '-' << i->second << endl;

    t_myVec markedForDeath;

    for (t_myMap::const_iterator it = mi.begin(); it != mi.end() ; it++)
        if (it->first > 2 && it->first < 5)
            markedForDeath.push_back(it->first);

    for(size_t i = 0; i < markedForDeath.size(); i++)
        // old erase, returns void...
        mi.erase(markedForDeath[i]);

    cout << "after old style erase of 3 & 4.." << endl;
    for(t_myMap::const_iterator i = mi.begin(); i != mi.end(); i++)
        cout << '\t' << i->first << '-' << i->second << endl;

    for (auto it = mi.begin(); it != mi.end(); ) {
        if (it->first == 5)
            // new erase() that returns iter..
            it = mi.erase(it);
        else
            ++it;
    }

    cout << "after new style erase of 5" << endl;
    // new cend/cbegin and lambda..
    for_each(mi.cbegin(), mi.cend(), [](t_myMap::const_reference it){cout << '\t' << it.first << '-' << it.second << endl;});

    return 0;
}

प्रिंट:

main() ENTRY
Init
        1-1
        2-1
        3-1
        4-1
        5-1
        6-1
after old style erase of 3 & 4..
        1-1
        2-1
        5-1
        6-1
after new style erase of 5
        1-1
        2-1
        6-1

Process returned 0 (0x0)   execution time : 0.021 s
Press any key to continue.

1
मुझे नहीं मिला। क्या समस्या है mi.erase(it++);?
लवलेला

1
@lvella सेशन देखें "अगर मैं map.erase का उपयोग करता हूं तो यह पुनरावृत्तियों को अमान्य कर देगा"।
कश्यप

मिटाने के बाद अगर आपका नया तरीका काम नहीं करेगा, तो नक्शा खाली हो जाएगा। उस स्थिति में, पुनरावृत्त अमान्य हो जाएगा। इसलिए, जल्द ही मिटाने के बाद, इसे सम्मिलित करना बेहतर है if(mi.empty()) break;
रहमत ज़मान

4

C ++ 20 ड्राफ्ट में सुविधा फ़ंक्शन होता है std::erase_if

तो आप इसे एक-लाइनर के रूप में करने के लिए उस फ़ंक्शन का उपयोग कर सकते हैं।

std::map<K, V> map_obj;
//calls needs_removing for each element and erases it, if true was reuturned
std::erase_if(map_obj,needs_removing);
//if you need to pass only part of the key/value pair
std::erase_if(map_obj,[](auto& kv){return needs_removing(kv.first);});

3

बहुत दुख की बात है, एह? जिस तरह से मैं आमतौर पर ऐसा करता हूं वह ट्रैवर्सल के दौरान हटाने के बजाय पुनरावृत्तियों के एक कंटेनर का निर्माण करता है। फिर कंटेनर के माध्यम से लूप करें और map.erase () का उपयोग करें

std::map<K,V> map;
std::list< std::map<K,V>::iterator > iteratorList;

for(auto i : map ){
    if ( needs_removing(i)){
        iteratorList.push_back(i);
    }
}
for(auto i : iteratorList){
    map.erase(*i)
}

लेकिन एक को मिटाने के बाद बाकी सभी अमान्य होंगे
दानी


@ दानी: नक्शे में नहीं। मानचित्र में त्रुटि केवल मिटाए गए आइटम के लिए पुनरावृत्ति को अमान्य करती है।
अंकलेंस

3

यदि यह आपकी प्रोग्रामिंग शैली के अनुरूप है, तो C ++ 11 को मानते हुए, यहां एक-लाइनर लूप बॉडी है:

using Map = std::map<K,V>;
Map map;

// Erase members that satisfy needs_removing(itr)
for (Map::const_iterator itr = map.cbegin() ; itr != map.cend() ; )
  itr = needs_removing(itr) ? map.erase(itr) : std::next(itr);

कुछ अन्य मामूली शैली में परिवर्तन:

  • Map::const_iteratorउपयोग करने पर, घोषित प्रकार ( ) जब संभव / सुविधाजनक हो, दिखाएं auto
  • usingटेम्पलेट प्रकार के लिए उपयोग करें , सहायक प्रकार ( Map::const_iterator) को पढ़ने / बनाए रखने में आसान बनाने के लिए।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.