C ++ में ऑपरेशन सेट करें (मौजूदा मूल्य अपडेट करें)


21

यहाँ मेरा कोड है:

 while (it!=s.end())  //here 's' is a set of stl and 'it' is iterator of set
    {   
        *it=*it-sub;    //'sub' is an int value
        it++;
    }

मैं पुनरावृति द्वारा सेट के मान को अपडेट नहीं कर सकता। मैं सेट के सभी तत्व से पूर्णांक मान 'सब' घटाना चाहता हूं।

क्या कोई मेरी मदद कर सकता है कि वास्तविक समस्या कहां है और वास्तविक समाधान क्या होगा?

यहाँ त्रुटि संदेश है:

error: assignment of read-only location it.std::_Rb_tree_const_iterator<int>::operator*()’
   28 |             *it=*it-sub;
      |             ~~~^~~~~~~~


9
सेट में तत्वों को केवल पढ़ा जा सकता है। एक को संशोधित करके, आप सेट में अन्य मदों को फिर से व्यवस्थित करेंगे।
रफिक्स ० r

3
समाधान यह है कि पुनरावृत्ति को मिटा दें और कुंजी के साथ एक नया डालें *it - sub। कृपया, ध्यान दें कि लूप को ठीक से काम std::set::erase()करने के लिए एक नया पुनरावृत्तिकर्ता जो आपके मामले में उपयोग किया जाना whileहै।
शेफ

2
@Scheff क्या आप ऐसा कर सकते हैं जब आप एक सेट पर ierating कर रहे हैं? क्या यह एक अंतहीन लूप में समाप्त नहीं हो सकता है? विशेष रूप से जब घुंघराले पुनरावृत्त सेट के लिए प्रासंगिक कुछ किया जाता है जो विज़िट किए गए तत्वों को डालता है जहां उन्हें फिर से दौरा किया जाएगा?
युनानोश

1
इम्तियाज जिज्ञासा से बाहर, इस असाइनमेंट के लिए एक अनुवर्ती है, क्या आप इसे यहां टिप्पणी में रिपोर्ट कर सकते हैं (मुझे लगता है कि यह एक असाइनमेंट है, जिसका अर्थ इसके बिना कुछ भी बुरा है, आपका प्रश्न ठीक है)? जैसा कि आप शेफ़ जवाब पर मेरी टिप्पणियों में देख सकते हैं, मैं इसके साथ शिक्षकों की बड़ी योजना के बारे में अनुमान लगा रहा हूं। केवल जानने की इच्छा।
युनानोश

जवाबों:


22

एक में तत्वों की कुंजी मान std::setरहे हैं constएक अच्छे कारण के लिए। उन्हें संशोधित करना उस आदेश को नष्ट कर सकता है जो एक के लिए आवश्यक है std::set

इसलिए, समाधान यह है कि पुनरावृत्त को मिटा दें और कुंजी के साथ एक नया डालें *it - sub। कृपया, ध्यान दें कि std::set::erase()एक नया पुनरावृत्त रिटर्न देता है जिसका उपयोग आपके मामले में किया जाता है, जबकि लूप ठीक से काम करने के लिए।

#include<iostream>
#include<set>

template <typename T>
std::ostream& operator<<(std::ostream &out, const std::set<T> &values)
{
  const char *sep = "{ ";
  for (const T &value : values) { out << sep << value; sep = ", "; }
  return out << " }";
}

int main()
{
  std::set<int> test{ 11, 12, 13, 14, 15 };
  std::cout << "test: " << test << '\n';
  const int sub = 10;
  std::set<int>::iterator iter = test.begin();
  while (iter != test.end()) {
    const int value = *iter;
    iter = test.erase(iter);
    test.insert(value - sub);
  }
  std::cout << "test: " << test << '\n';
}

आउटपुट:

test: { 11, 12, 13, 14, 15 }
test: { 1, 2, 3, 4, 5 }

कोलिरु पर लाइव डेमो


इस पर std::setपुनरावृत्ति करते समय परिवर्तन सामान्य रूप से समस्या नहीं है, लेकिन सूक्ष्म मुद्दों का कारण बन सकता है।

सबसे महत्वपूर्ण तथ्य यह है कि सभी उपयोग किए गए पुनरावृत्तियों को अक्षुण्ण रखा जाना चाहिए या अब उपयोग नहीं किया जा सकता है। (इसीलिए इरेज़ एलिमेंट का करंट इटरेटर रिटर्न वैल्यू के साथ असाइन किया गया है, std::set::erase()जो या तो एक इंटेक्ट इटरेटर है या सेट के अंत में है।)

बेशक, तत्वों को वर्तमान पुनरावृत्त के पीछे भी डाला जा सकता है। हालांकि यह एक समस्या नहीं है, क्योंकि std::setयह मेरे उपरोक्त उदाहरण के पाश को तोड़ सकता है।

इसे प्रदर्शित करने के लिए, मैंने उपरोक्त नमूने को थोड़ा बदल दिया। कृपया, ध्यान दें कि मैंने लूप को समाप्त करने के लिए एक अतिरिक्त काउंटर जोड़ा है:

#include<iostream>
#include<set>

template <typename T>
std::ostream& operator<<(std::ostream &out, const std::set<T> &values)
{
  const char *sep = "{ ";
  for (const T &value : values) { out << sep << value; sep = ", "; }
  return out << " }";
}

int main()
{
  std::set<int> test{ 11, 12, 13, 14, 15 };
  std::cout << "test: " << test << '\n';
  const int add = 10;
  std::set<int>::iterator iter = test.begin();
  int n = 7;
  while (iter != test.end()) {
    if (n-- > 0) {
      const int value = *iter;
      iter = test.erase(iter);
      test.insert(value + add);
    } else ++iter;
  }
  std::cout << "test: " << test << '\n';
}

आउटपुट:

test: { 11, 12, 13, 14, 15 }
test: { 23, 24, 25, 31, 32 }

कोलिरु पर लाइव डेमो


1
क्या यह संभव है कि यह केवल किसी चीज को घटाने के लिए काम करता है, लेकिन अगर यह एक ऐसी जगह पर पुनर्निमाण का कारण बनता है जहां यह बाद में दौरा किया जाएगा ... तो एक अंतहीन लूप में समाप्त हो सकता है?
युनानोश

@Yunnosch अनंत लूप के लिए खतरे के अलावा, वर्तमान पुनरावृत्ति के पीछे पुनरावृत्तियों को सम्मिलित करने के लिए कोई समस्या नहीं है। Iterators में स्थिर हैं std::set। सीमा मामले पर विचार करना आवश्यक हो सकता है कि नया पुनरावृत्त सीधे मिटाए जाने के पीछे डाला जाता है। - लूप में डालने के बाद इसे छोड़ दिया जाएगा।
शेफ

3
C ++ 17 में, आप extractनोड्स, उनकी कुंजियों को संशोधित कर सकते हैं, और उन्हें वापस सेट करने के लिए वापस कर सकते हैं। यह अधिक कुशल होगा, क्योंकि यह अनावश्यक आवंटन से बचता है।
डेनियल लैंगर

क्या आप विस्तृत कर सकते हैं "लूप में डालने के बाद इसे छोड़ दिया जाएगा।" मुझे लगता है कि मुझे आपकी बात वहां नहीं आती।
युनानोश

2
एक और मुद्दा यह है कि एक घटाए गए तत्व का मूल्य उसी में से एक हो सकता है जो अभी तक अप्रमाणित मूल्यों में से एक है std::set। चूंकि आपके पास एक ही तत्व दो बार नहीं हो सकता है, सम्मिलन बस std::setअपरिवर्तित छोड़ देगा और आप बाद में तत्व खो देंगे। उदाहरण के लिए इनपुट सेट पर विचार करें: {10, 20, 30}साथ add = 10
कॉमिकसंस

6

सरल बस एक और सेट के साथ इसे बदलने के लिए

std::set<int> copy;

for (auto i : s)
    copy.insert(i - sub);

s.swap(copy);

5

आप std::setडिज़ाइन के तत्वों को म्यूट नहीं कर सकते हैं । देख

https://en.cppreference.com/w/cpp/container/set/begin

चूँकि इटरेटर और कॉन्स्टेटर दोनों निरंतर चलने वाले होते हैं (और वास्तव में एक ही प्रकार के हो सकते हैं), इन में से किसी भी सदस्य फ़ंक्शन द्वारा लौटाए गए इटरेटर के माध्यम से कंटेनर के तत्वों को म्यूट करना संभव नहीं है।

ऐसा इसलिए है क्योंकि सेट को क्रमबद्ध किया गया है । यदि आप एक सॉर्ट किए गए संग्रह में तत्व को म्यूट करते हैं, तो संग्रह को फिर से सॉर्ट किया जाना चाहिए, जो निश्चित रूप से संभव है, लेकिन सी ++ तरीका नहीं है।

आपके विकल्प हैं:

  1. दूसरे प्रकार के संग्रह (अनसोल्ड) का उपयोग करें।
  2. एक नया सेट बनाएं और इसे संशोधित तत्वों से भरें।
  3. से एक तत्व निकालें std::set, इसे संशोधित करें, फिर डालें। (यदि आप हर तत्व को संशोधित करना चाहते हैं तो यह एक अच्छा विचार नहीं है)

4

एक std::setआम तौर पर एक आत्म संतुलन एसटीएल में द्विआधारी पेड़ के रूप में कार्यान्वित किया जाता है। *itउस तत्व का मूल्य है जो पेड़ को ऑर्डर करने के लिए उपयोग किया जाता है। यदि इसे संशोधित करना संभव था, तो आदेश अमान्य हो जाएगा इसलिए ऐसा करना संभव नहीं है।

यदि आप किसी तत्व को अपडेट करना चाहते हैं, तो आपको सेट में उस तत्व को ढूंढना होगा, इसे हटा दें और तत्व का अद्यतन मूल्य डालें। लेकिन चूंकि आपको सभी तत्वों के मूल्यों को अपडेट करना है, तो आपको सभी तत्वों को एक-एक करके मिटा देना होगा।

प्रदान किए गए लूप के लिए इसे एक में करना संभव है sub > 0S.erase(pos)स्थिति में पुनरावृत्ति को हटाता है posऔर निम्न स्थिति देता है। यदि sub > 0, आपके द्वारा डाला sub <= 0गया अद्यतन किया गया मान ट्री में नए पुनरावर्तक पर मान से पहले आएगा , लेकिन यदि , तो अद्यतित मान ट्री में नए पुनरावृत्त पर मान के बाद आएगा और इसलिए आप एक में समाप्त करेंगे अपरिमित गांठ।

for (auto itr = S.begin(); itr != S.end(); )
{
    int val = *itr;
    itr = S.erase(itr);
    S.insert(val - sub);
}

यह एक अच्छा तरीका है ... मुझे लगता है कि यह एकमात्र तरीका है कि हटाएं और फिर से डालें।
इम्तियाज मेहेदी

3

त्रुटि बहुत ज्यादा समस्या बताती है

std::setकंटेनर के सदस्य हैं const। उन्हें बदलने से उनके संबंधित ऑर्डर अमान्य हो जाते हैं।

में बदलते तत्वों के लिए std::set, आपको आइटम को मिटाना होगा और इसे बदलने के बाद फिर से डालना होगा।

वैकल्पिक रूप से, आप std::mapइस परिदृश्य को दूर करने के लिए उपयोग कर सकते हैं ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.