শুরু থেকে শুরু পর্যন্ত সি ++ ভেক্টরকে চিহ্নিত করা


101

শুরু থেকে শুরু পর্যন্ত কোনও ভেক্টরকে পুনরাবৃত্তি করা সম্ভব?

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

বা এরকম কিছু দিয়েই কেবল এটি সম্ভব:

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

4
সি ++ 11 এ আপনি বিপরীত অ্যাডাপ্টারের সাহায্যে লুপটি
এমএম

4
তাত্ত্বিকভাবে, একটি দ্বিতীয় বিট মেশিনে, দ্বিতীয় সমাধানের জন্য, যদি ভেক্টরের আকারটি 2,147,483,647 + 1 এর চেয়ে বড় হয় তবে এটি উপচে পড়বে (ভেক্টর :: আকার () স্বাক্ষরবিহীন), তবে বর্তমানে সম্ভাবনা রয়েছে যে আপনি কখনই এই সীমাটি আঘাত করবেন না (এছাড়াও 32 বিট মেশিনে বর্তমান ভেক্টরের সীমা 1,073,741,823)।
স্টেফান রোগিন

@ স্টেফানরোগিন ওভারফ্লো ইস্যুটি বাস্তব হয়ে যায় যখন লুপের জন্য "ইনট আই" এর পরিবর্তে কেউ তাদের সংকলক সতর্কতা (আকারের কারণে) (এন্টিমেটের অ্যাসাইনমেন্টের কারণে) এড়াতে সাইজে_ট (বা সম্ভবত অটো) ব্যবহার করতে পারে। এটির সাথে এবং একক উপাদান ভেক্টরের জন্য, দ্বিতীয় পুনরাবৃত্তিটি অটো আই ওভারফ্লো হয়ে যায় এবং লুপ ওভারফ্লাউন "i" দিয়ে কার্যকর করে যার ফলে সমস্ত ধরণের ক্র্যাশ হয়।
এন-ম্যাম

উত্তর:


164

সবচেয়ে ভাল উপায়:

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

rbegin()/ rend()বিশেষত সেই উদ্দেশ্যে ডিজাইন করা হয়েছিল। (এবং হ্যাঁ, একটি reverse_interatorচালকে পিছনে বাড়িয়ে তোলা))

এখন, তত্ত্ব হিসাবে, আপনার পদ্ধতি (ব্যবহার begin()/ end()& --i) কাজ করবে, std::vectorএর পুনরাবৃত্তিকে দ্বি-নির্দেশমূলক বলে মনে হচ্ছে, তবে মনে রাখবেন, 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

4
আপনি কেন do-whileকেবল একটি whileলুপের পরিবর্তে একটি লুপ ব্যবহার করছেন ? তারপরে আপনার খালি ভেক্টরগুলির জন্য কোনও বিশেষ চেকের প্রয়োজন হবে না।
জেমসডলিন

আপনি autoআরও ভাল পাঠযোগ্যতার জন্য উত্তরটি আপডেট করতে পারেন ?
এলএনজে

60

আপনার যদি সি ++ 11 থাকে তবে আপনি এটি ব্যবহার করতে পারেন auto

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

30

বন্ধ-খোলা রেঞ্জগুলির মাধ্যমে বিপরীত পুনরাবৃত্তির জন্য সু-প্রতিষ্ঠিত "প্যাটার্ন" নীচের মত দেখাচ্ছে

// 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
}

সিপ্রেফারেন্স ডট কম বলেছে, শেষে উপাদানটি অ্যাক্সেস করা () " অপরিজ্ঞাত আচরণে ফলাফল", তাই আমি মনে করি, লুপগুলি শুরু হওয়া উচিত--end()
টমাস শ্মিড

4
@ থমাস শ্মমিড এই লুপগুলি কখনও অ্যাক্সেস করার চেষ্টা করে না end()। যদিও তারা শুরু করে বলে মনে হচ্ছে end(), তারা সর্বদা প্রথম অ্যাক্সেসের আগে পুনরাবৃত্তিকে হ্রাস করতে নিশ্চিত করে।
এএনটি

এটি এতটা সুন্দর তবে রিবেগিন / রেন্ড করুন কারণ আপনি রানটাইম (অন্য কোনও টেম্পলেট নয়) এ অন্যভাবে লুপ করতে পারেন 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; }
কলিন

4
@ কোলিন ডিম! যে কুশ্রী!. আপনি 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আপনার প্রথমটি অন্য উপায়ে লুপ করে থাকলেও সক্ষম able
কলিন

17

সি ++ 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++)


6
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;

বিকল্পভাবে সি ++ 14 এ কেবল করুন:

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

সি ++ 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

ইয়াকের শেষে আমি পিছনের দিকের পুনরাবৃত্তিকে পছন্দ করি - অ্যাডাম নেভ্রামামন্টের উত্তর, তবে আমার যা প্রয়োজন তা জটিল মনে হয়েছিল, তাই আমি এটি লিখেছিলাম:

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
}

1

এখানে একটি দুর্দান্ত সরল বাস্তবায়ন যা প্রতিটি নির্মাণের জন্য ব্যবহারের অনুমতি দেয় এবং কেবল সি ++ 14 স্ট্যান্ডার্ড লাইব্রেরিতে নির্ভর করে:

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));
}

এটি এমন জিনিসগুলির সাথে কাজ করে যা একটি রিবেগিন সরবরাহ করে () এবং রেন্ড () সরবরাহ করে পাশাপাশি স্থির অ্যারেগুলি সরবরাহ করে।

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

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

1

আপনি যদি বুস্ট লাইব্রেরিটি ব্যবহার করতে পারেন তবে এখানে বুস্ট.রেঞ্জ রয়েছে যা অন্তর্ভুক্ত করে reverseরেঞ্জ অ্যাডাপ্টার সরবরাহ করে:

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

তারপরে, সি ++ 11 এর পরিসীমা forলুপের সাথে একত্রে আপনি কেবল নিচেরটি লিখতে পারেন:

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

যেহেতু এই কোডটি পুনরুক্তি জুটির ব্যবহারকারীর চেয়ে বিশুদ্ধতর, তাই মনোযোগ দেওয়ার মতো বিশদ কম থাকায় এটি তত বেশি পাঠযোগ্য এবং ত্রুটিগুলির প্রবণতা কম হতে পারে।


4
আসলে, boost::adaptors::reverseখুব দরকারী!
কাই পেটজেকে

-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;
}

4
এই কোডটি ভয়াবহভাবে ব্যর্থ হয়, যদি vecখালি ভেক্টরকে বোঝায়!
কাই পেটজ্কে

-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;

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

    std::cout << "\n************\n";
 
    if (arr.size() > 0) {
        // iterate backward, simple Joe version
        it = arr.end() - 1;
        while (it != arr.begin()) {
            std::cout << *it << " ";
            it--;
        }
        std::cout << *it << " ";
    } 

    // iterate backwards, the C++ way
    std::vector<int>::reverse_iterator rit;
    for (rit = arr.rbegin(); rit != arr.rend(); rit++) {
        std::cout << *rit << " ";
    }

    return 0;
}

এই কোডটি ভয়াবহভাবে ব্যর্থ হয়, যদি arrখালি ভেক্টরকে বোঝায়!
কাই পেটজ্কে
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.