সি ++ 11 লুপের জন্য বিপরীত পরিসীমা-ভিত্তিক


321

এমন একটি ধারক অ্যাডাপ্টার রয়েছে যা পুনরাবৃত্তির দিককে বিপরীত করবে যাতে আমি ল্যাপের সাথে রেঞ্জ-ভিত্তিক বিপরীতে কোনও ধারককে পুনরাবৃত্তি করতে পারি?

সুস্পষ্ট পুনরাবৃত্তির সাথে আমি এটি রূপান্তর করব:

for (auto i = c.begin(); i != c.end(); ++i) { ...

এটিতে:

for (auto i = c.rbegin(); i != c.rend(); ++i) { ...

আমি এটি রূপান্তর করতে চাই:

for (auto& i: c) { ...

এটি:

for (auto& i: std::magic_reverse_adapter(c)) { ...

এরকম কিছু আছে নাকি আমাকে নিজে লিখতে হবে?


17
একটি বিপরীত ধারক অ্যাডাপ্টার, আকর্ষণীয় শোনায় তবে আমি মনে করি এটি আপনাকে নিজেরাই লিখতে হবে। যদি আমাদের স্ট্যান্ডার্ড কমিটি তাড়াতাড়ি করে সুস্পষ্ট পুনরাবৃত্তির পরিবর্তে পরিসীমা ভিত্তিক অ্যালগরিদমগুলি গ্রহণ করে তবে আমাদের এই সমস্যা হবে না।
deft_code

4
@ ডিফ্ট_কোড: "পরিবর্তে?" কেন আপনি পুনরুক্তি ভিত্তিক অ্যালগোরিদম থেকে মুক্তি পেতে চান? তারা অনেক ভালো এবং কম ক্ষেত্রে যেখানে আপনি পুনরুক্তি থেকে না জন্য বাগাড়ম্বরপূর্ণ করছি beginকরার end, বা প্রবাহ iterators মত সঙ্গে তার আচরণ জন্য। ব্যাপ্তি অ্যালগরিদম দুর্দান্ত হবে তবে পুনরাবৃত্তাকারী অ্যালগরিদমগুলির তুলনায় এগুলি সত্যই কেবল সিনট্যাকটিক চিনি (অলস মূল্যায়নের সম্ভাবনা বাদে)।
নিকল বোলাস

17
@ ডিফ্ট_কোড template<typename T> class reverse_adapter { public: reverse_adapter(T& c) : c(c) { } typename T::reverse_iterator begin() { return c.rbegin(); } typename T::reverse_iterator end() { return c.rend(); } private: T& c; };এটি উন্নত করা যেতে পারে ( constসংস্করণ যোগ করা ইত্যাদি) তবে এটি কাজ করে: vector<int> v {1, 2, 3}; reverse_adapter<decltype(v)> ra; for (auto& i : ra) cout << i;প্রিন্টস321
শেঠ কার্নেগি

10
@ শেঠ কার্নেগি: এবং একটি দুর্দান্ত কার্যকরী ফর্ম যুক্ত করতে: template<typename T> reverse_adapter<T> reverse_adapt_container(T &c) {return reverse_adapter<T>(c);}সুতরাং আপনি কেবল for(auto &i: reverse_adapt_container(v)) cout << i;পুনরাবৃত্তি করতে পারেন ।
নিকল বোলাস

2
@ সিআর: আমি মনে করি না যে এর অর্থ হওয়া উচিত , কারণ এটি লুপগুলির সংক্ষিপ্ত বাক্য গঠন হিসাবে এটি অনুপলব্ধ করে তোলে যেখানে আদেশের বিষয়টি বিবেচনা করে। আইএমও সংক্ষিপ্ততা আপনার শব্দার্থক অর্থের চেয়ে আরও গুরুত্বপূর্ণ / দরকারী, তবে আপনি যদি সংক্ষিপ্ততার বিবেচনা না করেন তবে আপনার শৈলী নির্দেশিকা এটি যা খুশি তা আপনাকে দিতে পারে। এটা কি ধরনের parallel_forজন্য হবে একটি এমনকি আরো শক্তিশালী সঙ্গে "আমি কেয়ার করি না কি অর্ডার" শর্ত, যদি কোন না কোনভাবে মান অন্তর্ভুক্ত করা হয়। অবশ্যই এটিতে একটি পরিসীমা ভিত্তিক সিনট্যাকটিক চিনিও থাকতে পারে :-)
স্টিভ জেসোপ

উত্তর:


230

আসলে বুস্ট যেমন অ্যাডাপ্টার আছে: boost::adaptors::reverse

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

int main()
{
    std::list<int> x { 2, 3, 5, 7, 11, 13, 17, 19 };
    for (auto i : boost::adaptors::reverse(x))
        std::cout << i << '\n';
    for (auto i : x)
        std::cout << i << '\n';
}

90

আসলে, সি ++ 14 এ কোডের খুব কয়েকটি লাইন দিয়ে করা যায়।

এটি @ পল এর সমাধানের সাথে মত একটি খুব মিল similar সি ++ 11 থেকে নিখোঁজ জিনিসগুলির কারণে, সমাধানটি কিছুটা অহেতুক পুষ্পিত হয় (অতিরিক্ত গন্ধে সংজ্ঞায়িত)। C ++ 14 কে ধন্যবাদ আমরা এটিকে আরও অনেক বেশি পাঠযোগ্য করে তুলতে পারি।

মূল পর্যবেক্ষণটি হ'ল ব্যাপ্তির জন্য পরিসীমা ভিত্তিক লুপগুলি পুনরায় নির্ভর করে begin()এবং end()রেঞ্জটির পুনরাবৃত্তি অর্জন করতে পারে। এডিএলকে ধন্যবাদ , একজনকে এমনকি তাদের প্রথা begin()এবং end()স্ট্যান্ড :: নেমস্পেসের সংজ্ঞা দেওয়ার দরকার নেই ।

এখানে একটি খুব সাধারণ নমুনা সমাধান:

// -------------------------------------------------------------------
// --- Reversed iterable

template <typename T>
struct reversion_wrapper { T& iterable; };

template <typename T>
auto begin (reversion_wrapper<T> w) { return std::rbegin(w.iterable); }

template <typename T>
auto end (reversion_wrapper<T> w) { return std::rend(w.iterable); }

template <typename T>
reversion_wrapper<T> reverse (T&& iterable) { return { iterable }; }

উদাহরণস্বরূপ: এটি একটি কবজির মতো কাজ করে:

template <typename T>
void print_iterable (std::ostream& out, const T& iterable)
{
    for (auto&& element: iterable)
        out << element << ',';
    out << '\n';
}

int main (int, char**)
{
    using namespace std;

    // on prvalues
    print_iterable(cout, reverse(initializer_list<int> { 1, 2, 3, 4, }));

    // on const lvalue references
    const list<int> ints_list { 1, 2, 3, 4, };
    for (auto&& el: reverse(ints_list))
        cout << el << ',';
    cout << '\n';

    // on mutable lvalue references
    vector<int> ints_vec { 0, 0, 0, 0, };
    size_t i = 0;
    for (int& el: reverse(ints_vec))
        el += i++;
    print_iterable(cout, ints_vec);
    print_iterable(cout, reverse(ints_vec));

    return 0;
}

প্রত্যাশিত হিসাবে মুদ্রণ

4,3,2,1,
4,3,2,1,
3,2,1,0,
0,1,2,3,

দ্রষ্টব্য std::rbegin() , std::rend()এবং std::make_reverse_iterator()এখনও GCC-4.9 এ প্রয়োগ করা হয়নি। আমি আদর্শ অনুসারে এই উদাহরণগুলি লিখি, তবে তারা স্থিতিশীল জি ++ তে সংকলন করবে না। তবুও, এই তিনটি ফাংশনের জন্য অস্থায়ী স্টাবগুলি যুক্ত করা খুব সহজ। এখানে একটি নমুনা বাস্তবায়ন রয়েছে, অবশ্যই সম্পূর্ণ নয় তবে বেশিরভাগ ক্ষেত্রেই এটি যথেষ্ট ভালভাবে কাজ করে:

// --------------------------------------------------
template <typename I>
reverse_iterator<I> make_reverse_iterator (I i)
{
    return std::reverse_iterator<I> { i };
}

// --------------------------------------------------
template <typename T>
auto rbegin (T& iterable)
{
    return make_reverse_iterator(iterable.end());
}

template <typename T>
auto rend (T& iterable)
{
    return make_reverse_iterator(iterable.begin());
}

// const container variants

template <typename T>
auto rbegin (const T& iterable)
{
    return make_reverse_iterator(iterable.end());
}

template <typename T>
auto rend (const T& iterable)
{
    return make_reverse_iterator(iterable.begin());
}

35
কয়েক লাইনের কোড? আমাকে ক্ষমা করুন তবে এর দশের বেশি :-)
জনি

4
প্রকৃতপক্ষে, এটি 5-13, আপনি কীভাবে লাইনগুলি গণনা করেন তার উপর নির্ভর করে:) কাজের লাইব্রেরির অংশ হওয়ায় কাজের চারপাশে সেখানে থাকা উচিত নয়। বিটিডব্লিউ আমাকে স্মরণ করিয়ে দেওয়ার জন্য ধন্যবাদ, সাম্প্রতিক সংকলক সংস্করণগুলির জন্য এই উত্তরটি আপডেট করা দরকার, যেখানে অতিরিক্ত অতিরিক্ত সমস্ত রেখার প্রয়োজন নেই।
প্রিকসো এনএআই

2
আমি মনে করি আপনি forward<T>আপনার reverseপ্রয়োগে ভুলে গেছেন ।
স্নেক করুন

3
এইচএম, আপনি যদি এটি একটি শিরোনামে রাখেন তবে আপনি using namespace stdএকটি শিরোনামে রয়েছেন, এটি ভাল ধারণা নয়। নাকি আমি কিছু মিস করছি?
ইস্টান

3
আসলে, আপনি "<<<> ব্যবহার করে" লেখা উচিত নয়; একটি শিরোনামে ফাইল স্কোপ এ। আপনি প্রারম্ভিক () এবং শেষ () এর জন্য ফাংশন স্কোপে ব্যবহারের ঘোষণাগুলি সরিয়ে উপরেরটির উন্নতি করতে পারেন।
ক্রিস হার্টম্যান

23

এটি C ++ 11 এ কাজ না করা উচিত উত্সাহ ছাড়াই:

namespace std {
template<class T>
T begin(std::pair<T, T> p)
{
    return p.first;
}
template<class T>
T end(std::pair<T, T> p)
{
    return p.second;
}
}

template<class Iterator>
std::reverse_iterator<Iterator> make_reverse_iterator(Iterator it)
{
    return std::reverse_iterator<Iterator>(it);
}

template<class Range>
std::pair<std::reverse_iterator<decltype(begin(std::declval<Range>()))>, std::reverse_iterator<decltype(begin(std::declval<Range>()))>> make_reverse_range(Range&& r)
{
    return std::make_pair(make_reverse_iterator(begin(r)), make_reverse_iterator(end(r)));
}

for(auto x: make_reverse_range(r))
{
    ...
}

58
আইআইআরসি নামস্থান স্পেসে কিছু যুক্ত করা মহাকাব্য ব্যর্থতার আমন্ত্রণ fail
বিসিএস

35
আমি "মহাকাব্যিক ব্যর্থ" এর আদর্শিক অর্থ সম্পর্কে নিশ্চিত নই, তবে stdনেমস্পেসে কোনও ক্রিয়াকলাপকে ওভারলোড করার ক্ষেত্রে প্রতি 17.6.4.2.1 প্রতি অপরিবর্তিত আচরণ রয়েছে।
কেসি


6
@ মুহাম্মাদআন্নাকীব দুর্ভাগ্যজনক বিষয় হ'ল এটি করা ঠিক ধাক্কা খায়। আপনি উভয় সংজ্ঞা সংকলন করতে পারবেন না। প্লাস কম্পাইলার আছে সংজ্ঞা প্রয়োজন নেই না সি ++ অধীনে 11 উপস্থিত হতে হবে এবং সি ++ 14 বছরের কম শুধুমাত্র প্রদর্শিত (বৈশিষ্ট কি সম্পর্কে কিছু বলে না না এসটিডি :: নামস্থানে হয় ঠিক কি)। সুতরাং এটি একটি মান-সম্মতিযুক্ত সি ++ 11 সংকলকটির অধীনে খুব সম্ভবত সংকলন ব্যর্থতা হতে পারে ... এটির কিছু এলোমেলো নাম যা সি ++ 14 এ ছিল না তার চেয়ে অনেক বেশি সম্ভবত ! এবং যেমনটি উল্লেখ করা হয়েছে, এটি "অপরিজ্ঞাত আচরণ" ... তাই সংকলন করতে ব্যর্থ হওয়া এটি সবচেয়ে খারাপ হতে পারে না।
হোস্টাইলফোর্ক বলেছেন যে এসই

2
@HostileFork কোন নাম সংঘর্ষের নেই, make_reverse_iteratorনা হয় stdনামস্থান, তাই এটি সি সঙ্গে ++, এটা 14 সংস্করণ সংঘর্ষ করা হবে না।
পল ফুলতজ দ্বিতীয়

11

এটি কি তোমার জন্য কাজ করে:

#include <iostream>
#include <list>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/iterator_range.hpp>

int main(int argc, char* argv[]){

  typedef std::list<int> Nums;
  typedef Nums::iterator NumIt;
  typedef boost::range_reverse_iterator<Nums>::type RevNumIt;
  typedef boost::iterator_range<NumIt> irange_1;
  typedef boost::iterator_range<RevNumIt> irange_2;

  Nums n = {1, 2, 3, 4, 5, 6, 7, 8};
  irange_1 r1 = boost::make_iterator_range( boost::begin(n), boost::end(n) );
  irange_2 r2 = boost::make_iterator_range( boost::end(n), boost::begin(n) );


  // prints: 1 2 3 4 5 6 7 8 
  for(auto e : r1)
    std::cout << e << ' ';

  std::cout << std::endl;

  // prints: 8 7 6 5 4 3 2 1
  for(auto e : r2)
    std::cout << e << ' ';

  std::cout << std::endl;

  return 0;
}

7
template <typename C>
struct reverse_wrapper {

    C & c_;
    reverse_wrapper(C & c) :  c_(c) {}

    typename C::reverse_iterator begin() {return c_.rbegin();}
    typename C::reverse_iterator end() {return c_.rend(); }
};

template <typename C, size_t N>
struct reverse_wrapper< C[N] >{

    C (&c_)[N];
    reverse_wrapper( C(&c)[N] ) : c_(c) {}

    typename std::reverse_iterator<const C *> begin() { return std::rbegin(c_); }
    typename std::reverse_iterator<const C *> end() { return std::rend(c_); }
};


template <typename C>
reverse_wrapper<C> r_wrap(C & c) {
    return reverse_wrapper<C>(c);
}

উদাহরণ:

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

    for (auto i : r_wrap(arr)) {
        printf("%d ", i);
    }
    printf("\n");

    for (auto i : r_wrap(arr1)) {
        printf("%d ", i);
    }
    printf("\n");
    return 0;
}

1
আপনি দয়া করে আপনার উত্তর আরও বিস্তারিত ব্যাখ্যা করতে পারেন?
মোস্তাফিজ

এটি একটি বিপরীত পরিসীমা-বেস লুপ সি ++ 11 শ্রেণির ট্যাম্পলেট
খান লউ

4

আপনি যদি পরিসর v3 ব্যবহার করতে পারেন তবে আপনি বিপরীত পরিসীমা অ্যাডাপ্টার ব্যবহার করতে পারেনranges::view::reverse দেখতে দেয়।

সর্বনিম্ন কাজের উদাহরণ:

#include <iostream>
#include <vector>
#include <range/v3/view.hpp>

int main()
{
    std::vector<int> intVec = {1, 2, 3, 4, 5, 6, 7, 8, 9};

    for (auto const& e : ranges::view::reverse(intVec)) {
        std::cout << e << " ";   
    }
    std::cout << std::endl;

    for (auto const& e : intVec) {
        std::cout << e << " ";   
    }
    std::cout << std::endl;
}

ডেমো 1 দেখুন ।

দ্রষ্টব্য: এরিক নিবলার হিসাবে , এই বৈশিষ্ট্যটি সি ++ 20 এ উপলব্ধ হবে । এটি <experimental/ranges/range>হেডার সহ ব্যবহার করা যেতে পারে can তারপরে forবিবৃতিটি দেখতে পাবেন:

for (auto const& e : view::reverse(intVec)) {
       std::cout << e << " ";   
}

ডেমো 2 দেখুন


আপডেট: ranges::viewনাম স্থানটির নামকরণ করা হয়েছে ranges::views। সুতরাং, ব্যবহার করুন ranges::views::reverse
nac001

2

যদি C ++ 14 ব্যবহার না করে থাকেন তবে আমি সহজ সমাধানটির নীচে খুঁজে পাচ্ছি।

#define METHOD(NAME, ...) auto NAME __VA_ARGS__ -> decltype(m_T.r##NAME) { return m_T.r##NAME; }
template<typename T>
struct Reverse
{
  T& m_T;

  METHOD(begin());
  METHOD(end());
  METHOD(begin(), const);
  METHOD(end(), const);
};
#undef METHOD

template<typename T>
Reverse<T> MakeReverse (T& t) { return Reverse<T>{t}; }

ডেমো
এটি পাত্রে / ডেটা-ধরণের (অ্যারের মতো) begin/rbegin, end/rendকাজ করে না, যার কোনও কার্যকারিতা নেই।


0

আপনি কেবল BOOST_REVERSE_FOREACHপিছনে পিছনে পুনরাবৃত্তি যা ব্যবহার করতে পারেন । উদাহরণস্বরূপ, কোড

#include <iostream>
#include <boost\foreach.hpp>

int main()
{
    int integers[] = { 0, 1, 2, 3, 4 };
    BOOST_REVERSE_FOREACH(auto i, integers)
    {
        std::cout << i << std::endl;
    }
    return 0;
}

নিম্নলিখিত আউটপুট উত্পন্ন:

4

3

2

1

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