अंत से शुरुआत तक C ++ वेक्टर को Iterating


96

क्या शुरू से अंत तक एक वेक्टर को पुनरावृत्त करना संभव है?

for (vector<my_class>::iterator i = my_vector.end();
        i != my_vector.begin(); /* ?! */ ) {
}

या यह केवल इस तरह से कुछ के साथ संभव है:

for (int i = my_vector.size() - 1; i >= 0; --i) {
}

2
C ++ 11 में आप रिवर्स एडॉप्टर के साथ रेंज-आधारित फॉर-लूप का उपयोग कर सकते हैं, यहां देखें
MM

1
सैद्धांतिक रूप से, 32 बिट मशीन पर, दूसरे समाधान के लिए, यदि वेक्टर का आकार 2,147,483,647 + 1 से बड़ा है, तो यह अतिप्रवाह (वेक्टर :: आकार () अहस्ताक्षरित) होगा, लेकिन वर्तमान में संभावना है कि आप इस सीमा को कभी नहीं मारेंगे () 32 बिट मशीनों पर वर्तमान वेक्टर सीमा 1,073,741,823 है)।
स्टीफन रोजिन

जवाबों:


157

सबसे अच्छा तरीका है:

for (vector<my_class>::reverse_iterator i = my_vector.rbegin(); 
        i != my_vector.rend(); ++i ) { 
} 

rbegin()/ rend()विशेष रूप से उस उद्देश्य के लिए डिजाइन किए गए थे। (और हाँ, reverse_interatorयह एक कदम बढ़ाकर पिछड़ा हुआ है।)

अब, सिद्धांत रूप में, अपने विधि (का उपयोग करते हुए begin()/ end()& --i), काम करेगा std::vector's इटरेटर द्विदिश किया जा रहा है, लेकिन याद रखें, end()पिछले तत्व नहीं है - यह पिछले तत्व से परे एक है, तो आप पहली बार घटती करना होगा, और आप कर रहे हैं जब आप पहुंचते हैं तब किया जाता है begin()- लेकिन आपको अभी भी अपना प्रसंस्करण करना है।

vector<my_class>::iterator i = my_vector.end();
while (i != my_vector.begin())
{
     --i;
    /*do stuff */

} 

अद्यतन: मैं स्पष्ट रूप से for()लूप में लूप को फिर से लिखने में बहुत आक्रामक था while()। (महत्वपूर्ण हिस्सा यह है कि --iशुरुआत में है।)


मुझे बस एहसास हुआ कि --iकंटेनर खाली होने पर एक बड़ी समस्या पैदा हो जाएगी ... do - whileलूप में जाने से पहले यह जांचने के लिए समझ में आता है (my_vector.begin() != my_vector.end())
a1ex07

1
आप do-whileसिर्फ लूप के बजाय लूप का उपयोग क्यों कर रहे हैं while? फिर आपको खाली वैक्टर के लिए किसी विशेष जांच की आवश्यकता नहीं होगी।
jamesdlin

क्या आप autoबेहतर पठनीयता के लिए उपयोग के उत्तर को अपडेट कर सकते हैं ?
एलएनजे

58

यदि आपके पास C ++ 11 है तो आप इसका उपयोग कर सकते हैं auto

for (auto it = my_vector.rbegin(); it != my_vector.rend(); ++it)
{
}

28

बंद-खुली श्रेणियों के माध्यम से रिवर्स-इट्रेटिंग के लिए अच्छी तरह से स्थापित "पैटर्न" निम्नानुसार है

// Iterate over [begin, end) range in reverse
for (iterator = end; iterator-- != begin; ) {
  // Process `*iterator`
}

या, यदि आप चाहें,

// Iterate over [begin, end) range in reverse
for (iterator = end; iterator != begin; ) {
  --iterator;
  // Process `*iterator`
}

यह पैटर्न उपयोगी है, उदाहरण के लिए, एक अहस्ताक्षरित सूचकांक का उपयोग करके किसी सरणी को रिवर्स-इंडेक्स करने के लिए

int array[N];
...
// Iterate over [0, N) range in reverse
for (unsigned i = N; i-- != 0; ) {
  array[i]; // <- process it
}

(इस पैटर्न से अपरिचित लोग अक्सर विशेष रूप से सरणी अनुक्रमण के लिए हस्ताक्षरित पूर्णांक प्रकारों का उपयोग करने पर जोर देते हैं क्योंकि वे गलत तरीके से मानते हैं कि अहस्ताक्षरित प्रकार रिवर्स अनुक्रमण के लिए किसी तरह "अनुपयोगी" हैं)

इसका उपयोग "स्लाइडिंग पॉइंटर" तकनीक का उपयोग करके एक सरणी पर पुनरावृत्ति के लिए किया जा सकता है

// Iterate over [array, array + N) range in reverse
for (int *p = array + N; p-- != array; ) {
  *p; // <- process it
}

या इसे एक साधारण (रिवर्स नहीं) पुनरावृत्ति का उपयोग करके वेक्टर पर रिवर्स-इटरेशन के लिए उपयोग किया जा सकता है

for (vector<my_class>::iterator i = my_vector.end(); i-- != my_vector.begin(); ) {
  *i; // <- process it
}

cppreference.com कहता है, तत्व को अंत में एक्सेस करना () "अपरिभाषित व्यवहार का परिणाम" है, इसलिए मुझे लगता है, छोरों को शुरू होना चाहिए--end()
थॉमस श्मिट

@ThomasSchmid ये लूप कभी भी एक्सेस करने का प्रयास नहीं करते हैं end()। भले ही वे शुरू करने के लिए लग रहे हों end(), वे हमेशा पहली पहुंच से पहले पुनरावृत्ति करने वाले को सुनिश्चित करना चाहते हैं।
चींटी

यह बहुत अच्छा है तो rbegin / रेंडरिंग क्योंकि आप रनटाइम (कोई टेम्पलेट) पर दूसरे तरीके से लूप कर सकते हैं auto a = vector<int>{0,1,2}; bool reversed = 0; auto it = (!reversed?a.begin():a.end()); auto end = (reversed?a.begin():a.end()); while(it != end) { if(reversed)--it; cout << *it << endl; if(!reversed)++it; }
कॉलिन

@ कोलिन Egads! वो बदसूरत!। आप reversed चार बार परीक्षण कर रहे हैं - उनमें से दो एक लूप के अंदर। बेशक, एक बूलियन का परीक्षण करना बहुत तेज़ है, लेकिन फिर भी, आपको काम क्यों नहीं करना है? विशेष रूप से, चूंकि एकमात्र उद्देश्य कोड को अपठनीय बनाना प्रतीत होता है। कैसे हम दो अलग-अलग छोरों का उपयोग करते हैं? if (reversed) for (auto it = my_vector.rbegin(); it != my_vector.rend(); ++it) {doStuff(*it);} else for (auto it = my_vector.begin(); it != my_vector.end(); ++it) {doStuff(*it);}
जेम्स क्यूरन

वास्तव में तुम मेरी बात से चूक गए। आप इसे दो ifएस में विभाजित करने के लिए पूरी तरह से सही हैं, लेकिन मैं इस टेम्पलेट से छुटकारा पाना चाहता था doStuff()। फिर भी उल्लेखनीय है कि दो के साथ ifआप पहले रास्ते पर दूसरे रास्ते को गोल कर रहे हैं।
कॉलिन

11

C ++ 20 से शुरू करते हुए, आप एक std::ranges::reverse_viewऔर रेंज-आधारित फॉर-लूप का उपयोग कर सकते हैं :

#include<ranges>
#include<vector>
#include<iostream>

using namespace std::ranges;

std::vector<int> const vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

for(auto& i :  views::reverse(vec)) {
    std::cout << i << ",";
}

या और भी

for(auto& i :  vec | views::reverse)

दुर्भाग्य से, लेखन के समय (जनवरी 2020) कोई भी बड़ा संकलक रेंज लाइब्रेरी को लागू नहीं करता है, लेकिन आप एरिक नेब्लर की पर्वतमाला-वी 3 का सहारा ले सकते हैं :

#include <iostream>
#include <vector>
#include "range/v3/all.hpp"

int main() {

    using namespace ranges;

    std::vector<int> const vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    for(auto& i :  views::reverse(vec)) {
        std::cout << i << ",";
    }

    return 0;
}

9

उपयोगकर्ता rend() / rbegin()पुनरावृत्तियों:

for (vector<myclass>::reverse_iterator it = myvector.rbegin(); it != myvector.rend(); it++)


5
template<class It>
std::reverse_iterator<It> reversed( It it ) {
  return std::reverse_iterator<It>(std::forward<It>(it));
}

फिर:

for( auto rit = reversed(data.end()); rit != reversed(data.begin()); ++rit ) {
  std::cout << *rit;

वैकल्पिक रूप से C ++ 14 में बस:

for( auto rit = std::rbegin(data); rit != std::rend(data); ++rit ) {
  std::cout << *rit;

C ++ 03/11 में अधिकांश मानक कंटेनरों में एक .rbegin()और .rend()विधि है।

अंत में, आप रेंज एडेप्टर backwardsको इस प्रकार लिख सकते हैं:

namespace adl_aux {
  using std::begin; using std::end;
  template<class C>
  decltype( begin( std::declval<C>() ) ) adl_begin( C&& c ) {
    return begin(std::forward<C>(c));
  }
  template<class C>
  decltype( end( std::declval<C>() ) ) adl_end( C&& c ) {
    return end(std::forward<C>(c));
  }
}

template<class It>
struct simple_range {
  It b_, e_;
  simple_range():b_(),e_(){}
  It begin() const { return b_; }
  It end() const { return e_; }
  simple_range( It b, It e ):b_(b), e_(e) {}

  template<class OtherRange>
  simple_range( OtherRange&& o ):
    simple_range(adl_aux::adl_begin(o), adl_aux::adl_end(o))
  {}

  // explicit defaults:
  simple_range( simple_range const& o ) = default;
  simple_range( simple_range && o ) = default;
  simple_range& operator=( simple_range const& o ) = default;
  simple_range& operator=( simple_range && o ) = default;
};
template<class C>
simple_range< decltype( reversed( adl_aux::adl_begin( std::declval<C&>() ) ) ) >
backwards( C&& c ) {
  return { reversed( adl_aux::adl_end(c) ), reversed( adl_aux::adl_begin(c) ) };
}

और अब आप यह कर सकते हैं:

for (auto&& x : backwards(ctnr))
  std::cout << x;

जो मुझे लगता है कि काफी सुंदर है।



1

यहां एक सुपर सरल कार्यान्वयन है जो प्रत्येक निर्माण के लिए उपयोग करने की अनुमति देता है और केवल C ++ 14 std लाइब्रेरी पर निर्भर करता है:

namespace Details {

    // simple storage of a begin and end iterator
    template<class T>
    struct iterator_range
    {
        T beginning, ending;
        iterator_range(T beginning, T ending) : beginning(beginning), ending(ending) {}

        T begin() const { return beginning; }
        T end() const { return ending; }
    };

}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// usage:
//  for (auto e : backwards(collection))
template<class T>
auto backwards(T & collection)
{
    using namespace std;
    return Details::iterator_range(rbegin(collection), rend(collection));
}

यह उन चीजों के साथ काम करता है जो एक rbegin () और रेंडरिंग (), साथ ही साथ स्थिर सरणियों की आपूर्ति करता है।

std::vector<int> collection{ 5, 9, 15, 22 };
for (auto e : backwards(collection))
    ;

long values[] = { 3, 6, 9, 12 };
for (auto e : backwards(values))
    ;

0

मैं याक्क के अंत में पीछे की ओर चलने वाले को पसंद करता हूं - एडम नेवरुमोंट का जवाब, लेकिन यह जटिल लगता था कि मुझे क्या चाहिए, इसलिए मैंने यह लिखा:

template <class T>
class backwards {
    T& _obj;
public:
    backwards(T &obj) : _obj(obj) {}
    auto begin() {return _obj.rbegin();}
    auto end() {return _obj.rend();}
};

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

for (auto &elem : vec) {
    // ... my useful code
}

और इसे उल्टा करने के लिए इसे बदल दें:

for (auto &elem : backwards(vec)) {
    // ... my useful code
}

0

यदि आप बूस्ट लाइब्रेरी का उपयोग कर सकते हैं, तो Boost.Range है जो reverseरेंज एडेप्टर को शामिल करके प्रदान करता है :

#include <boost/range/adaptor/reversed.hpp>

फिर, C ++ 11 के रेंज- forलूप के साथ संयोजन में , आप बस निम्नलिखित लिख सकते हैं:

for (auto& elem: boost::adaptors::reverse(my_vector)) {
   // ...
}

चूंकि यह कोड पुनरावृत्ति जोड़ी का उपयोग करने वाले की तुलना में अधिक सुरक्षित है, इसलिए यह अधिक पठनीय और त्रुटियों के लिए कम प्रवण हो सकता है क्योंकि इस पर ध्यान देने के लिए कम विवरण हैं।


-1

इस कोड का उपयोग करें

//print the vector element in reverse order by normal iterator.
cout <<"print the vector element in reverse order by normal iterator." <<endl;
vector<string>::iterator iter=vec.end();
--iter;
while (iter != vec.begin())
{
    cout << *iter  << " "; 
    --iter;
}

-2

के रूप में मैं विदेशी मंगल ग्रह नए वाक्यविन्यास का परिचय देना नहीं चाहता जो भी, और मैं बस मौजूदा आदिम पर निर्माण करना चाहता हूं, नीचे दिए गए स्निपेट काम करने लगते हैं:

#include <vector>
#include <iostream>

int main (int argc,char *argv[])
{
    std::vector<int> arr{1,2,3,4,5};
    std::vector<int>::iterator it;

    for (it = arr.begin(); it != arr.end(); it++) {
        std::cout << *it << " ";
    }

    std::cout << "\n************\n";

    for (it = arr.end() - 1; it != arr.begin()-1;it--) {
        std::cout << *it << " ";
    }

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