c++ - असमान आकार के साथ भागों में एक सरणी को विभाजित करने के लिए इटरेटर का उपयोग करना




iterator modulo (2)

मेरे पास एक सरणी है जिसे मुझे 3-तत्व सब-एरे में विभाजित करना होगा। मैं इसे इटरेटर्स के साथ करना चाहता था, लेकिन मैं सरणी के अंत में और फिर सेगेटिंग को खत्म करना शुरू कर देता हूं, भले ही मैं इरेटरेटर की अनुपयुक्तता नहीं करता । दिए गए: auto foo = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; मैं कर रहा हूँ:

auto bar = cbegin(foo);

for (auto it = next(bar, 3); it < foo.end(); bar = it, it = next(bar, 3)) {
    for_each(bar, it, [](const auto& i) { cout << i << endl; });
}

for_each(bar, cend(foo), [](const auto& i) { cout << i << endl; });

अब मैं एक finish पुनरावर्तक परिभाषित करके इसे हल कर सकता हूँ:

auto bar = cbegin(foo);
auto finish = next(cend(foo), -(size(foo) % 3));

for (auto it = next(bar, 3); it != finish; bar = it, it = next(bar, 3)) {
    for_each(bar, it, [](const auto& i) { cout << i << endl; });
}

for_each(bar, finish, [](const auto& i) { cout << i << endl; });
for_each(finish, cend(foo), [](const auto& i) { cout << i << endl; });

लेकिन यह अनावश्यक लगता है जब मैं इरेटरेटर की कमी नहीं करता । मैं पहले संस्करण क्यों नहीं कर सकता?


आपके द्वारा देखे जा रहे segfault next जांच के लिए आ रहा है जो आपके लिए डिबग कार्यान्वयन में एक अभियोग है जो अपरिभाषित व्यवहार से जांचने के लिए है। इटरेटर और पॉइंटर्स का व्यवहार उनकी आवंटित रेंज से परे परिभाषित नहीं है, और "एक अतीत-अंत-एंट" तत्व: क्या "एक अतीत-द-अंत" इटरेटर अनिर्धारित व्यवहार से गुजर रहा है?

इसका मतलब यह है कि "एक अतीत-अंत-अंत" तत्व के अतीत को बढ़ाना अनिवार्य व्यवहार है जो इटरेटर के बाद के उपयोग से स्वतंत्र है । व्यवहार को परिभाषित करने के लिए आपको अपने पूर्णांक मोडुलो एल्गोरिथ्म या इसी तरह के समाधान का उपयोग करना चाहिए , लेकिन आपको कम से कम अपने उप-आकार के आकार की उपलब्धता के आधार पर कुछ ऐसी स्थिति में auto it = next(bar, 3) को बदलना होगा, जो कि auto it = next(bar, 3) -अरे, तो ऐसा कुछ: auto it = size(foo) <= 3 ? finish : next(bar, 3) एफू auto it = size(foo) <= 3 ? finish : next(bar, 3) auto it = size(foo) <= 3 ? finish : next(bar, 3)

जहां सबसे अच्छा उपाय यहां उपलब्ध है, वहां कम से कम बेमानी चलने का कारण एक कंटेनर में शेष आकार को एक पूर्णांक के रूप में ट्रैक करना है जो अपरिभाषित व्यवहार से ग्रस्त नहीं होता है, जब यह सीमा से बाहर हो जाता है और "एक अतीत-अंत-अंत" इसके द्वारा पूरा किया जा सकता है:

auto bar = cbegin(foo);

for (auto i = size(foo); i > STEP; i -= STEP) {
    for(auto j = 0; j < STEP; ++j, ++bar) cout << *bar << '\t';
    cout << endl;
}

for(auto i = 0; j < STEP; ++j, ++bar) cout << *bar << '\t';
cout << endl;

संपादित करें: मैंने पहले उन पॉइंटर्स का सुझाव दिया था जो डिबग वातानुकूलित नहीं हैं, यह अपरिभाषित व्यवहार है।

समस्या यह है कि next आपके लिए सीमा की जांच कर रही है हम आवंटित स्मृति के बाहर हर समय पॉइंटर्स का उपयोग करते हैं, उदाहरण के लिए, nullptr और end , और it सब यहाँ है यदि आप बस सी-शैली पॉइंटर अंकगणित का उपयोग करते हैं तो आप ठीक हो जाएंगे:

auto bar = cbegin(foo);

for (auto it = bar + 3; it < cend(foo); bar = it, it = bar + 3) {
    for_each(bar, it, [](const auto& i) { cout << i << endl; });
}

for_each(bar, cend(foo), [](const auto& i) { cout << '\t' << i << endl; });

लाइव उदाहरण

वैकल्पिक रूप से, यदि आप रिलीज कॉन्फ़िगरेशन में चलाते हैं, तो सीमा जांच को निकाला जाना चाहिए, इसलिए आप अपने कोड के पहले संस्करण का उपयोग करने में सक्षम होंगे।


इसका निषिद्ध कारण आपके दूसरे प्रश्न पर अच्छी तरह से कवर किया गया है क्या "एक अतीत-द-अंत" इटरेटर अपर्याप्त व्यवहार से गुजर रहे हैं? इसलिए मैं सिर्फ बेहतर समाधान का समाधान करूंगा

यादृच्छिक-पहुंच वाले यंत्रों के लिए (जो आपके पास होना चाहिए अगर आप < ) का उपयोग कर रहे हों, महंगी मॉड्यूलो संचालन के लिए कोई भी आवश्यकता नहीं है

मुख्य बिंदु यह है कि:

  • it + stride विफल रहता है जब it अंत के करीब है
  • end() - stride यदि कंटेनर में बहुत कम तत्व शामिल हैं तो end() - stride विफल हो जाती है
  • end() - it हमेशा कानूनी है

वहां से, यह बीजीय सरगर्मी है, इसे बदलने के लिए it + stride < end() एक कानूनी रूप में (दोनों पक्षों से घटाना)

अंतिम परिणाम, जिसे मैंने कई बार उपयोग किया है:

for( auto it = c.cbegin(), end = c.cend(); end - it >= stride; it += stride )

कंपाइलर एक प्रीकॉम्प्टेड end - stride * sizeof(*it) तुलना करने के लिए ऑप्टिमाइज़ करने के लिए स्वतंत्र है end - stride * sizeof(*it) यदि मेमोरी मॉडल सपाट है - सी ++ व्यवहार की सीमाएं आदिम परिचालनों पर लागू नहीं होती हैं जो कम्पाइलर में सी ++ का अनुवाद करता है।

आप निश्चित रूप से std::distance(it, end) उपयोग कर सकते हैं यदि आप ऑपरेटरों के बजाय नामित फ़ंक्शंस का उपयोग करना पसंद करते हैं, लेकिन यह केवल यादृच्छिक-पहुंच व्यास के लिए कुशल होगा।

अग्रेषित Iterators के साथ उपयोग के लिए, आपको ऐसी कुछ का उपयोग करना चाहिए जो वेतन वृद्धि और समाप्ति की स्थिति को जोड़ती है जैसे कि

struct less_preferred { size_t value; less_preferred(size_t v) : value(v){} };

template<typename Iterator>
bool try_advance( Iterator& it, less_preferred step, Iterator end )
{
     while (step.value--) {
         if (it == end) return false;
         ++it;
     }
     return true;
}

इस अतिरिक्त अधिभार के साथ, आप यादृच्छिक-पहुंच व्यास के लिए कुशल व्यवहार प्राप्त करेंगे:

template<typename RandomIterator>
auto try_advance( RandomIterator& it, size_t stride, RandomIterator end )
     -> decltype(end - it < stride) // SFINAE
{
     if (end - it < stride) return false;
     it += stride;
     return true;
}






data-partitioning